node-opcua-address-space 2.89.0 → 2.90.1

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 (33) hide show
  1. package/dist/source/loader/load_nodeset2.js +5 -5
  2. package/dist/source/loader/load_nodeset2.js.map +1 -1
  3. package/dist/src/base_node_private.js +6 -6
  4. package/dist/src/base_node_private.js.map +1 -1
  5. package/dist/src/check_value_rank_compatibility.d.ts +15 -0
  6. package/dist/src/check_value_rank_compatibility.js +82 -0
  7. package/dist/src/check_value_rank_compatibility.js.map +1 -0
  8. package/dist/src/extension_object_array_node.js +11 -0
  9. package/dist/src/extension_object_array_node.js.map +1 -1
  10. package/dist/src/namespace_impl.js +2 -3
  11. package/dist/src/namespace_impl.js.map +1 -1
  12. package/dist/src/nodeset_tools/nodeset_to_xml.js +6 -1
  13. package/dist/src/nodeset_tools/nodeset_to_xml.js.map +1 -1
  14. package/dist/src/ua_variable_impl.d.ts +12 -0
  15. package/dist/src/ua_variable_impl.js +66 -14
  16. package/dist/src/ua_variable_impl.js.map +1 -1
  17. package/dist/src/ua_variable_impl_ext_obj.d.ts +2 -0
  18. package/dist/src/ua_variable_impl_ext_obj.js +66 -21
  19. package/dist/src/ua_variable_impl_ext_obj.js.map +1 -1
  20. package/dist/src/ua_variable_type_impl.d.ts +11 -1
  21. package/dist/src/ua_variable_type_impl.js +6 -0
  22. package/dist/src/ua_variable_type_impl.js.map +1 -1
  23. package/package.json +34 -34
  24. package/source/loader/load_nodeset2.ts +11 -10
  25. package/src/base_node_private.ts +7 -7
  26. package/src/check_value_rank_compatibility.ts +79 -0
  27. package/src/extension_object_array_node.ts +11 -2
  28. package/src/namespace_impl.ts +4 -7
  29. package/src/nodeset_tools/nodeset_to_xml.ts +9 -4
  30. package/src/ua_variable_impl.ts +82 -19
  31. package/src/ua_variable_impl_ext_obj.ts +79 -26
  32. package/src/ua_variable_type_impl.ts +20 -1
  33. package/test_helpers/test_fixtures/eurange_issue.xml +3 -2
@@ -86,6 +86,8 @@ import { apply_condition_refresh, ConditionRefreshCache } from "./apply_conditio
86
86
  import {
87
87
  extractPartialData,
88
88
  incrementElement,
89
+ propagateTouchValueDownward,
90
+ propagateTouchValueDownwardArray,
89
91
  propagateTouchValueUpward,
90
92
  setExtensionObjectPartialValue,
91
93
  _bindExtensionObject,
@@ -245,6 +247,17 @@ function default_func(this: UAVariable, dataValue1: DataValue, callback1: Callba
245
247
  interface UAVariableOptions extends InternalBaseNodeOptions {
246
248
  value?: any;
247
249
  dataType: NodeId | string;
250
+ /**
251
+ * This attribute indicates whether the Value attribute of the Variableis an array and how many dimensions the array has.
252
+ * It may have the following values:
253
+ * * n > 1: the Value is an array with the specified number of dimensions.
254
+ * * OneDimension (1): The value is an array with one dimension.
255
+ * * OneOrMoreDimensions (0): The value is an array with one or more dimensions.
256
+ * * Scalar (−1): The value is not an array.
257
+ * * Any (−2): The value can be a scalar or an array with any number of dimensions.
258
+ * * ScalarOrOneDimension (−3): The value can be a scalar or a one dimensional array.
259
+ * * All DataTypes are considered to be scalar, even if they have array-like semantics like ByteString and String.
260
+ */
248
261
  valueRank?: number;
249
262
  arrayDimensions?: null | number[];
250
263
  accessLevel?: any;
@@ -286,6 +299,7 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
286
299
  private _basicDataType?: DataType;
287
300
 
288
301
  public $extensionObject?: any;
302
+ public $set_ExtensionObject?: (newValue: ExtensionObject, sourceTimestamp: PreciseClock, cache: Set<UAVariableImpl>) => void;
289
303
 
290
304
  public $historicalDataConfiguration?: UAHistoricalDataConfiguration;
291
305
  public varHistorian?: IVariableHistorian;
@@ -312,6 +326,7 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
312
326
  get typeDefinitionObj(): UAVariableType {
313
327
  // istanbul ignore next
314
328
  if (super.typeDefinitionObj && super.typeDefinitionObj.nodeClass !== NodeClass.VariableType) {
329
+ console.log(super.typeDefinitionObj.toString());
315
330
  throw new Error(
316
331
  "Invalid type definition node class , expecting a VariableType got " + NodeClass[super.typeDefinitionObj.nodeClass]
317
332
  );
@@ -524,6 +539,7 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
524
539
 
525
540
  public isExtensionObject(): boolean {
526
541
  // DataType must be one of Structure
542
+ if (this.dataType.isEmpty()) return false;
527
543
  const dataTypeNode = this.addressSpace.findDataType(this.dataType) as UADataType;
528
544
  if (!dataTypeNode) {
529
545
  throw new Error(" Cannot find DataType " + this.dataType.toString() + " in standard address Space");
@@ -572,9 +588,9 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
572
588
  dataValue.serverPicoseconds = 0;
573
589
  return callback(null, dataValue);
574
590
  }
575
-
576
591
  try {
577
592
  this.refreshFunc.call(this, (err: Error | null, dataValue?: DataValueLike) => {
593
+ // istanbul ignore next
578
594
  if (err || !dataValue) {
579
595
  errorLog(
580
596
  "-------------- refresh call failed",
@@ -814,19 +830,20 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
814
830
  if (dataValue.value.dataType === DataType.ExtensionObject) {
815
831
  const valueIsCorrect = this.checkExtensionObjectIsCorrect(dataValue.value.value);
816
832
  if (!valueIsCorrect) {
817
- errorLog("Invalid value !");
833
+ errorLog("setValueFromSource Invalid value !");
818
834
  errorLog(this.toString());
819
835
  errorLog(dataValue.toString());
820
836
  this.checkExtensionObjectIsCorrect(dataValue.value.value);
821
837
  }
822
- this.$dataValue = dataValue;
823
838
  // ----------------------------------
824
- if (this.$extensionObject) {
839
+ if (this.$extensionObject || this.$$extensionObjectArray) {
825
840
  // we have an extension object already bound to this node
826
841
  // the client is asking us to replace the object entierly by a new one
827
842
  // const ext = dataValue.value.value;
828
843
  this._internal_set_dataValue(dataValue);
829
844
  return;
845
+ } else {
846
+ this.$dataValue = dataValue;
830
847
  }
831
848
  } else {
832
849
  this._internal_set_dataValue(dataValue);
@@ -1283,6 +1300,7 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
1283
1300
  const n = callbacks.length;
1284
1301
  for (const callback1 of callbacks) {
1285
1302
  callback1.call(this, err, dataValue);
1303
+
1286
1304
  }
1287
1305
  };
1288
1306
 
@@ -1487,12 +1505,21 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
1487
1505
  optionalExtensionObject?: ExtensionObject | ExtensionObject[],
1488
1506
  options?: BindExtensionObjectOptions
1489
1507
  ): ExtensionObject | ExtensionObject[] | null {
1508
+
1509
+ // coerce to ExtensionObject[] when this.valueRank === 1
1510
+ if (optionalExtensionObject && this.valueRank === 1 && !Array.isArray(optionalExtensionObject) && optionalExtensionObject instanceof ExtensionObject) {
1511
+ warningLog("bindExtensionObject: coerce to ExtensionObject[] when this.valueRank === 1");
1512
+ optionalExtensionObject = [optionalExtensionObject];
1513
+ }
1514
+
1490
1515
  if (optionalExtensionObject) {
1491
1516
  if (optionalExtensionObject instanceof Array) {
1492
- assert(this.valueRank >= 1, "expecting an Array of Matrix variable here");
1517
+ assert(this.valueRank >= 1, "bindExtensionObject: expecting an Array of Matrix variable here");
1493
1518
  return _bindExtensionObjectArrayOrMatrix(this, optionalExtensionObject, options);
1494
1519
  } else {
1495
- assert(this.valueRank === -1, "expecting an Scalar variable here");
1520
+ if (this.valueRank !== -1 && this.valueRank !== 0) {
1521
+ throw new Error("bindExtensionObject: expecting an Scalar variable here but got value rank " + this.valueRank);
1522
+ }
1496
1523
  return _bindExtensionObject(this, optionalExtensionObject, options) as ExtensionObject;
1497
1524
  }
1498
1525
  }
@@ -1712,7 +1739,7 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
1712
1739
  throw new Error("Invalid Extension Object on nodeId =" + this.nodeId.toString());
1713
1740
  }
1714
1741
  }
1715
-
1742
+
1716
1743
  this.verifyVariantCompatibility(dataValue.value);
1717
1744
 
1718
1745
  this._inner_replace_dataValue(dataValue, indexRange);
@@ -1722,23 +1749,59 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
1722
1749
  * @private
1723
1750
  */
1724
1751
  public _inner_replace_dataValue(dataValue: DataValue, indexRange?: NumericRange | null) {
1725
- const old_dataValue = this.$dataValue;
1726
1752
 
1727
- this.$dataValue = dataValue;
1728
- this.$dataValue.statusCode = this.$dataValue.statusCode || StatusCodes.Good;
1753
+ assert(this.$dataValue.value instanceof Variant);
1754
+ const old_dataValue = this.$dataValue.clone();
1729
1755
 
1756
+ if (this.$$extensionObjectArray && dataValue.value.arrayType !== VariantArrayType.Scalar) {
1757
+ // we have a bounded array or matrix
1758
+ assert(Array.isArray(dataValue.value.value));
1759
+ if (this.$$extensionObjectArray !== this.$dataValue.value.value) {
1760
+ throw new Error("internal error");
1761
+ }
1762
+ this.$$extensionObjectArray = dataValue.value.value;
1763
+ this.$dataValue.value.value = dataValue.value.value;
1764
+
1765
+ this.$dataValue.statusCode = dataValue.statusCode || StatusCodes.Good;
1766
+ this.$dataValue.serverTimestamp = dataValue.serverTimestamp;
1767
+ this.$dataValue.serverPicoseconds = dataValue.serverPicoseconds;
1768
+ this.$dataValue.sourceTimestamp = dataValue.sourceTimestamp;
1769
+ this.$dataValue.sourcePicoseconds = dataValue.sourcePicoseconds;
1770
+
1771
+ } else if (this._basicDataType === DataType.ExtensionObject && this.valueRank === -1 && this.$set_ExtensionObject && dataValue.value.arrayType === VariantArrayType.Scalar) {
1772
+ // the entire extension object is changed.
1773
+ this.$dataValue.statusCode = this.$dataValue.statusCode || StatusCodes.Good;
1774
+ const preciseClock = coerceClock(this.$dataValue.sourceTimestamp, this.$dataValue.sourcePicoseconds);
1775
+ this.$set_ExtensionObject(dataValue.value.value, preciseClock, new Set())
1776
+ } else {
1777
+ this.$dataValue = dataValue;
1778
+ this.$dataValue.statusCode = this.$dataValue.statusCode || StatusCodes.Good;
1779
+ }
1730
1780
  // repair missing timestamps
1781
+ const now = new Date();
1731
1782
  if (!dataValue.serverTimestamp) {
1732
- this.$dataValue.serverTimestamp = old_dataValue.serverTimestamp;
1733
- this.$dataValue.serverPicoseconds = old_dataValue.serverPicoseconds;
1783
+ this.$dataValue.serverTimestamp = old_dataValue.serverTimestamp || now;
1784
+ this.$dataValue.serverPicoseconds = old_dataValue.serverPicoseconds || 0;
1734
1785
  }
1735
1786
  if (!dataValue.sourceTimestamp) {
1736
- this.$dataValue.sourceTimestamp = old_dataValue.sourceTimestamp;
1737
- this.$dataValue.sourcePicoseconds = old_dataValue.sourcePicoseconds;
1787
+ this.$dataValue.sourceTimestamp = old_dataValue.sourceTimestamp || now;
1788
+ this.$dataValue.sourcePicoseconds = old_dataValue.sourcePicoseconds || 0;
1738
1789
  }
1739
1790
 
1740
1791
  if (!sameDataValue(old_dataValue, dataValue)) {
1741
- this.emit("value_changed", this.$dataValue, indexRange);
1792
+ if (this.getBasicDataType() === DataType.ExtensionObject) {
1793
+ const preciseClock = coerceClock(this.$dataValue.sourceTimestamp, this.$dataValue.sourcePicoseconds);
1794
+ const cache: Set<UAVariable> = new Set();
1795
+ if (this.$$extensionObjectArray) {
1796
+ this.touchValue(preciseClock);
1797
+ propagateTouchValueDownwardArray(this, preciseClock, cache);
1798
+ } else {
1799
+ this.touchValue(preciseClock);
1800
+ propagateTouchValueDownward(this, preciseClock, cache);
1801
+ }
1802
+ } else {
1803
+ this.emit("value_changed", this.$dataValue, indexRange);
1804
+ }
1742
1805
  }
1743
1806
  }
1744
1807
 
@@ -2067,7 +2130,7 @@ function _Variable_bind_with_simple_get(this: UAVariableImpl, options: GetterOpt
2067
2130
  this._get_func = options.get;
2068
2131
 
2069
2132
  const timestamped_get_func_from__Variable_bind_with_simple_get = () => {
2070
- const value = this._get_func();
2133
+ const value: Variant | StatusCode = this._get_func();
2071
2134
 
2072
2135
  /* istanbul ignore next */
2073
2136
  if (!is_Variant_or_StatusCode(value)) {
@@ -2081,10 +2144,10 @@ function _Variable_bind_with_simple_get(this: UAVariableImpl, options: GetterOpt
2081
2144
  );
2082
2145
  }
2083
2146
  if (is_StatusCode(value)) {
2084
- return new DataValue({ statusCode: value });
2147
+ return new DataValue({ statusCode: value as StatusCode });
2085
2148
  } else {
2086
- if (!this.$dataValue || !this.$dataValue.statusCode.isGoodish() || !sameVariant(this.$dataValue.value, value)) {
2087
- this.setValueFromSource(value, StatusCodes.Good);
2149
+ if (!this.$dataValue || !this.$dataValue.statusCode.isGoodish() || !sameVariant(this.$dataValue.value, value as Variant)) {
2150
+ this._inner_replace_dataValue(new DataValue({ value }));
2088
2151
  }
2089
2152
  return this.$dataValue;
2090
2153
  }
@@ -104,12 +104,12 @@ export function _touchValue(property: UAVariableImpl, now: PreciseClock): void {
104
104
  property.$dataValue.serverTimestamp = now.timestamp;
105
105
  property.$dataValue.serverPicoseconds = now.picoseconds;
106
106
  property.$dataValue.statusCode = StatusCodes.Good;
107
- if (property.minimumSamplingInterval === 0) {
108
- if (property.listenerCount("value_changed") > 0) {
109
- const clonedDataValue = property.readValue();
110
- property.emit("value_changed", clonedDataValue);
111
- }
107
+ // if (property.minimumSamplingInterval === 0) {
108
+ if (property.listenerCount("value_changed") > 0) {
109
+ const clonedDataValue = property.readValue();
110
+ property.emit("value_changed", clonedDataValue);
112
111
  }
112
+ // }
113
113
  }
114
114
 
115
115
  export function propagateTouchValueUpward(self: UAVariableImpl, now: PreciseClock, cache?: Set<UAVariable>): void {
@@ -126,7 +126,7 @@ export function propagateTouchValueUpward(self: UAVariableImpl, now: PreciseCloc
126
126
  }
127
127
  }
128
128
 
129
- function propagateTouchValueDownward(self: UAVariableImpl, now: PreciseClock, cache?: Set<UAVariable>): void {
129
+ export function propagateTouchValueDownward(self: UAVariableImpl, now: PreciseClock, cache?: Set<UAVariable>): void {
130
130
  if (!self.isExtensionObject()) return;
131
131
  // also propagate changes to embeded variables
132
132
  const dataTypeNode = self.getDataTypeNode();
@@ -265,7 +265,7 @@ function installExt(uaVariable: UAVariableImpl, ext: ExtensionObject) {
265
265
  return uaVariable.getComponentByName(field.name!) as UAVariable | null;
266
266
  }));
267
267
  } else {
268
- warningLog("extension object is null");
268
+ doDebug && warningLog("extension object is null");
269
269
  }
270
270
  }
271
271
  }
@@ -336,7 +336,7 @@ function _installFields2(uaVariable: UAVariableImpl, { get, set }: {
336
336
  propertyNode.$dataValue.serverPicoseconds = uaVariable.$dataValue.serverPicoseconds;
337
337
  propertyNode.$dataValue.value.dataType = propertyNode.dataTypeObj.basicDataType;
338
338
  propertyNode.$dataValue.value.arrayType = propertyNode.valueRank === -1 ? VariantArrayType.Scalar : (propertyNode.valueRank === 1 ? VariantArrayType.Array : VariantArrayType.Matrix)
339
- propertyNode.$dataValue.value.dimensions =propertyNode.valueRank > 1 ? propertyNode.arrayDimensions : null;
339
+ propertyNode.$dataValue.value.dimensions = propertyNode.valueRank > 1 ? propertyNode.arrayDimensions : null;
340
340
 
341
341
  const fieldName = field.name!;
342
342
  installDataValueGetter(propertyNode, () => get(fieldName));
@@ -398,7 +398,7 @@ function isVariableContainingExtensionObject(uaVariable: UAVariableImpl): boolea
398
398
  function _innerBindExtensionObjectScalar(uaVariable: UAVariableImpl,
399
399
  { get, set }: {
400
400
  get: () => ExtensionObject;
401
- set: (value: ExtensionObject, sourceTimestamp: PreciseClock) => void;
401
+ set: (value: ExtensionObject, sourceTimestamp: PreciseClock, cache: Set<UAVariableImpl>) => void;
402
402
  },
403
403
  options?: BindExtensionObjectOptions
404
404
  ) {
@@ -412,13 +412,7 @@ function _innerBindExtensionObjectScalar(uaVariable: UAVariableImpl,
412
412
  };
413
413
 
414
414
  installDataValueGetter(uaVariable, get);
415
- assert(uaVariable._inner_replace_dataValue);
416
- uaVariable._inner_replace_dataValue = (dataValue: DataValue, indexRange?: NumericRange | null) => {
417
- /** */
418
- const ext = dataValue.value.value;
419
- const sourceTime = coerceClock(dataValue.sourceTimestamp, dataValue.sourcePicoseconds);
420
- set(ext, sourceTime);
421
- }
415
+ uaVariable.$set_ExtensionObject = set;
422
416
 
423
417
  _installFields2(uaVariable, {
424
418
  get: (fieldName: string) => {
@@ -436,6 +430,7 @@ function _innerBindExtensionObjectScalar(uaVariable: UAVariableImpl,
436
430
  }
437
431
 
438
432
 
433
+ // eslint-disable-next-line complexity
439
434
  export function _bindExtensionObject(
440
435
  uaVariable: UAVariableImpl,
441
436
  optionalExtensionObject?: ExtensionObject,
@@ -443,10 +438,16 @@ export function _bindExtensionObject(
443
438
  ): ExtensionObject | null {
444
439
  options = options || { createMissingProp: false };
445
440
 
441
+ // istanbul ignore next
446
442
  if (!isVariableContainingExtensionObject(uaVariable)) {
447
443
  return null;
448
444
  }
449
445
 
446
+ // istanbul ignore next
447
+ if (optionalExtensionObject && uaVariable.valueRank === 0) {
448
+ warningLog(uaVariable.browseName.toString() + ": valueRank was zero but needed to be adjusted to -1 (Scalar) in bindExtensionObject");
449
+ uaVariable.valueRank = -1;
450
+ }
450
451
  const addressSpace = uaVariable.addressSpace;
451
452
  let extensionObject_;
452
453
 
@@ -567,12 +568,16 @@ const composeBrowseNameAndNodeId = (uaVariable: UAVariable, indexes: number[]) =
567
568
  }
568
569
 
569
570
 
570
- // eslint-disable-next-line max-statements
571
+ // eslint-disable-next-line max-statements, complexity
571
572
  export function _bindExtensionObjectArrayOrMatrix(
572
573
  uaVariable: UAVariableImpl,
573
574
  optionalExtensionObjectArray?: ExtensionObject[],
574
575
  options?: BindExtensionObjectOptions
575
576
  ): ExtensionObject[] {
577
+
578
+ options = options || { createMissingProp: false};
579
+ options.createMissingProp = options.createMissingProp || false;
580
+
576
581
  // istanbul ignore next
577
582
  if (uaVariable.valueRank < 1) {
578
583
  throw new Error("Variable must be a MultiDimensional array");
@@ -582,6 +587,12 @@ export function _bindExtensionObjectArrayOrMatrix(
582
587
  if (!isVariableContainingExtensionObject(uaVariable)) {
583
588
  return [];
584
589
  }
590
+
591
+ if (!optionalExtensionObjectArray && uaVariable.$dataValue.value.value) {
592
+ assert(Array.isArray(uaVariable.$dataValue.value.value));
593
+ optionalExtensionObjectArray = uaVariable.$dataValue.value.value;
594
+ }
595
+
585
596
  if ((arrayDimensions.length === 0 || arrayDimensions.length === 1 && arrayDimensions[0] === 0) && optionalExtensionObjectArray) {
586
597
  arrayDimensions[0] = optionalExtensionObjectArray.length;
587
598
  }
@@ -607,6 +618,21 @@ export function _bindExtensionObjectArrayOrMatrix(
607
618
  uaVariable.$dataValue.value.dataType = DataType.ExtensionObject;
608
619
  uaVariable.$dataValue.value.value = uaVariable.$$extensionObjectArray;
609
620
 
621
+
622
+ // make sure uaVariable.$dataValue cannot be inadvertantly changed from this point onward
623
+ const $dataValue = uaVariable.$dataValue;
624
+ Object.defineProperty(uaVariable, "$dataValue", {
625
+ get(): DataValue {
626
+ return $dataValue;
627
+ },
628
+ set() {
629
+ throw new Error("$dataValue is now sealed , you should not change internal $dataValue!");
630
+ },
631
+ // writable: true,
632
+ enumerable: true,
633
+ configurable: true,
634
+ });
635
+
610
636
  uaVariable.bindVariable({
611
637
  get: () => uaVariable.$dataValue.value
612
638
  }, true);
@@ -620,10 +646,13 @@ export function _bindExtensionObjectArrayOrMatrix(
620
646
 
621
647
  const { browseName, nodeId } = composeBrowseNameAndNodeId(uaVariable, index);
622
648
 
623
-
624
-
625
649
  let uaElement = uaVariable.getComponentByName(browseName) as UAVariableImpl | null;
626
650
  if (!uaElement) {
651
+
652
+ if (!options.createMissingProp) {
653
+ continue;
654
+ }
655
+
627
656
  uaElement = namespace.addVariable({
628
657
  browseName,
629
658
  nodeId,
@@ -634,7 +663,6 @@ export function _bindExtensionObjectArrayOrMatrix(
634
663
  }) as UAVariableImpl;
635
664
  }
636
665
 
637
- uaElement.$dataValue.value.dataType = DataType.ExtensionObject;
638
666
  uaElement.$dataValue.statusCode = StatusCodes.Good;
639
667
  uaElement.$dataValue.sourceTimestamp = uaVariable.$dataValue.sourceTimestamp;
640
668
  uaElement.$dataValue.sourcePicoseconds = uaVariable.$dataValue.sourcePicoseconds;
@@ -645,16 +673,21 @@ export function _bindExtensionObjectArrayOrMatrix(
645
673
 
646
674
  {
647
675
  const capturedIndex = i;
676
+ const capturedUaElement = uaElement as UAVariableImpl;
648
677
  _innerBindExtensionObjectScalar(uaElement,
649
678
  {
650
679
  get: () => uaVariable.$$extensionObjectArray[capturedIndex],
651
- set: (newValue: ExtensionObject, sourceTimestamp: PreciseClock) => {
652
- uaVariable.$$extensionObjectArray[capturedIndex] = newValue;
653
- uaVariable.touchValue();
654
- propagateTouchValueDownward(uaVariable, sourceTimestamp);
655
- propagateTouchValueUpward(uaVariable, sourceTimestamp);
656
- },
680
+ set: (newValue: ExtensionObject, sourceTimestamp: PreciseClock, cache: Set<UAVariableImpl>) => {
657
681
 
682
+ assert(!isProxy(uaVariable.$$extensionObjectArray[capturedIndex]));
683
+ uaVariable.$$extensionObjectArray[capturedIndex] = newValue;
684
+ if (uaVariable.$$extensionObjectArray !== uaVariable.$dataValue.value.value) {
685
+ console.log("uaVariable", uaVariable.nodeId.toString());
686
+ console.log("Houston! We have a problem ");
687
+ }
688
+ propagateTouchValueDownward(capturedUaElement, sourceTimestamp, cache);
689
+ propagateTouchValueUpward(capturedUaElement, sourceTimestamp, cache);
690
+ }
658
691
  }, { ...options, force: true });
659
692
 
660
693
  }
@@ -717,3 +750,23 @@ export function extractPartialData(path: string | string[], extensionObject: Ext
717
750
  c1[name] = c2[name];
718
751
  return partialData;
719
752
  }
753
+
754
+ export function propagateTouchValueDownwardArray(uaVariable: UAVariableImpl, now: PreciseClock, cache: Set<UAVariable>) {
755
+
756
+ if (!uaVariable.$$extensionObjectArray) return;
757
+ const arrayDimensions = uaVariable.arrayDimensions || [];
758
+ const totalLength = uaVariable.$$extensionObjectArray.length;
759
+
760
+ const indexIterator = new IndexIterator(arrayDimensions);
761
+ for (let i = 0; i < totalLength; i++) {
762
+
763
+ const index = indexIterator.next();
764
+
765
+ const { browseName, nodeId } = composeBrowseNameAndNodeId(uaVariable, index);
766
+ const uaElement = uaVariable.getComponentByName(browseName) as UAVariableImpl | null;
767
+ if (uaElement?.nodeClass === NodeClass.Variable) {
768
+ uaElement.touchValue(now);
769
+ propagateTouchValueDownward(uaElement, now, cache);
770
+ }
771
+ }
772
+ }
@@ -40,6 +40,7 @@ import { _clone_children_references, ToStringBuilder, UAVariableType_toString }
40
40
  import * as tools from "./tool_isSupertypeOf";
41
41
  import { get_subtypeOfObj } from "./tool_isSupertypeOf";
42
42
  import { get_subtypeOf } from "./tool_isSupertypeOf";
43
+ import { checkValueRankCompatibility } from "./check_value_rank_compatibility";
43
44
 
44
45
  const debugLog = make_debugLog(__filename);
45
46
  const doDebug = checkDebugFlag(__filename);
@@ -50,6 +51,7 @@ const errorLog = make_errorLog(__filename);
50
51
  let doTrace = checkDebugFlag("INSTANTIATE");
51
52
  const traceLog = errorLog;
52
53
 
54
+
53
55
  interface InstantiateS {
54
56
  propertyOf?: any;
55
57
  componentOf?: any;
@@ -83,7 +85,17 @@ export function topMostParentIsObjectTypeOrVariableType(addressSpace: AddressSpa
83
85
  return false;
84
86
  }
85
87
  export interface UAVariableTypeOptions extends InternalBaseNodeOptions {
86
- /** */
88
+ /**
89
+ * This attribute indicates whether the Value attribute of the Variableis an array and how many dimensions the array has.
90
+ * It may have the following values:
91
+ * * n > 1: the Value is an array with the specified number of dimensions.
92
+ * * OneDimension (1): The value is an array with one dimension.
93
+ * * OneOrMoreDimensions (0): The value is an array with one or more dimensions.
94
+ * * Scalar (−1): The value is not an array.
95
+ * * Any (−2): The value can be a scalar or an array with any number of dimensions.
96
+ * * ScalarOrOneDimension (−3): The value can be a scalar or a one dimensional array.
97
+ * * All DataTypes are considered to be scalar, even if they have array-like semantics like ByteString and String.
98
+ */
87
99
  valueRank?: number;
88
100
  arrayDimensions?: number[] | null;
89
101
  historizing?: boolean;
@@ -227,6 +239,13 @@ export class UAVariableTypeImpl extends BaseNodeImpl implements UAVariableType {
227
239
  assert(dataType instanceof NodeId);
228
240
 
229
241
  const valueRank = options.valueRank !== undefined ? options.valueRank : this.valueRank;
242
+
243
+ const { result, errorMessage } = checkValueRankCompatibility(valueRank, this.valueRank);
244
+ if (!result) {
245
+ errorLog(errorMessage);
246
+ throw new Error(errorMessage);
247
+ }
248
+
230
249
  const arrayDimensions = options.arrayDimensions !== undefined ? options.arrayDimensions : this.arrayDimensions;
231
250
 
232
251
  // istanbul ignore next
@@ -18,6 +18,7 @@
18
18
  <Alias Alias="HasProperty">i=46</Alias>
19
19
  <Alias Alias="HasSubtype">i=45</Alias>
20
20
  <Alias Alias="HasTypeDefinition">i=40</Alias>
21
+ <Alias Alias="PropertyType">i=68</Alias>
21
22
  <Alias Alias="Int32">i=6</Alias>
22
23
  <Alias Alias="Range">i=884</Alias>
23
24
  <Alias Alias="String">i=12</Alias>
@@ -33,7 +34,7 @@
33
34
 
34
35
  <UAVariable NodeId="ns=1;i=1301" BrowseName="Range" AccessLevel="3" DataType="Range">
35
36
  <References>
36
- <Reference ReferenceType="HasTypeDefinition">i=1317</Reference>
37
+ <Reference ReferenceType="HasTypeDefinition">ns=1;i=1317</Reference>
37
38
  <Reference ReferenceType="HasModellingRule">i=78</Reference>
38
39
  </References>
39
40
  <Value>
@@ -54,7 +55,7 @@
54
55
  <UAVariableType NodeId="ns=1;i=1317" BrowseName="MyVariableType" AccessLevel="3" DataType="Range">
55
56
  <DisplayName>EURange</DisplayName>
56
57
  <References>
57
- <Reference ReferenceType="HasSubtype" IsForward="false">i=68</Reference>
58
+ <Reference ReferenceType="HasSubtype" IsForward="false">PropertyType</Reference>
58
59
  </References>
59
60
  <Value>
60
61
  <ExtensionObject>