node-opcua-address-space 2.57.0 → 2.61.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 (88) hide show
  1. package/dist/source/address_space_ts.d.ts +0 -2
  2. package/dist/source/helpers/argument_list.js +12 -1
  3. package/dist/source/helpers/argument_list.js.map +1 -1
  4. package/dist/source/helpers/multiform_func.d.ts +11 -0
  5. package/dist/source/helpers/multiform_func.js +74 -0
  6. package/dist/source/helpers/multiform_func.js.map +1 -0
  7. package/dist/source/loader/load_nodeset2.js +47 -64
  8. package/dist/source/loader/load_nodeset2.js.map +1 -1
  9. package/dist/source/set_namespace_meta_data.js +1 -1
  10. package/dist/src/address_space.js +12 -6
  11. package/dist/src/address_space.js.map +1 -1
  12. package/dist/src/alarms_and_conditions/condition_snapshot.js +3 -3
  13. package/dist/src/alarms_and_conditions/condition_snapshot.js.map +1 -1
  14. package/dist/src/alarms_and_conditions/ua_acknowledgeable_condition_impl.js +2 -1
  15. package/dist/src/alarms_and_conditions/ua_acknowledgeable_condition_impl.js.map +1 -1
  16. package/dist/src/alarms_and_conditions/ua_alarm_condition_impl.js +1 -1
  17. package/dist/src/alarms_and_conditions/ua_alarm_condition_impl.js.map +1 -1
  18. package/dist/src/alarms_and_conditions/ua_condition_impl.js +8 -6
  19. package/dist/src/alarms_and_conditions/ua_condition_impl.js.map +1 -1
  20. package/dist/src/alarms_and_conditions/ua_off_normal_alarm_impl.js +1 -1
  21. package/dist/src/alarms_and_conditions/ua_off_normal_alarm_impl.js.map +1 -1
  22. package/dist/src/base_node_impl.js +2 -0
  23. package/dist/src/base_node_impl.js.map +1 -1
  24. package/dist/src/base_node_private.d.ts +3 -3
  25. package/dist/src/base_node_private.js +198 -25
  26. package/dist/src/base_node_private.js.map +1 -1
  27. package/dist/src/event_data.js +1 -1
  28. package/dist/src/event_data.js.map +1 -1
  29. package/dist/src/namespace_impl.js +5 -5
  30. package/dist/src/namespace_impl.js.map +1 -1
  31. package/dist/src/nodeset_tools/nodeset_to_xml.js +15 -9
  32. package/dist/src/nodeset_tools/nodeset_to_xml.js.map +1 -1
  33. package/dist/src/nodeset_tools/typedictionary_to_xml.js +17 -10
  34. package/dist/src/nodeset_tools/typedictionary_to_xml.js.map +1 -1
  35. package/dist/src/reference_impl.js +1 -1
  36. package/dist/src/reference_impl.js.map +1 -1
  37. package/dist/src/state_machine/ua_shelving_state_machine_ex.js +20 -13
  38. package/dist/src/state_machine/ua_shelving_state_machine_ex.js.map +1 -1
  39. package/dist/src/ua_data_type_impl.d.ts +15 -5
  40. package/dist/src/ua_data_type_impl.js +129 -51
  41. package/dist/src/ua_data_type_impl.js.map +1 -1
  42. package/dist/src/ua_method_impl.d.ts +3 -2
  43. package/dist/src/ua_method_impl.js +7 -1
  44. package/dist/src/ua_method_impl.js.map +1 -1
  45. package/dist/src/ua_object_impl.js +2 -1
  46. package/dist/src/ua_object_impl.js.map +1 -1
  47. package/dist/src/ua_variable_impl.d.ts +12 -18
  48. package/dist/src/ua_variable_impl.js +285 -215
  49. package/dist/src/ua_variable_impl.js.map +1 -1
  50. package/dist/src/ua_variable_type_impl.d.ts +3 -4
  51. package/dist/src/ua_variable_type_impl.js +60 -52
  52. package/dist/src/ua_variable_type_impl.js.map +1 -1
  53. package/dist/src/ua_view_impl.js +1 -1
  54. package/dist/src/ua_view_impl.js.map +1 -1
  55. package/distHelpers/add_event_generator_object.js.map +1 -1
  56. package/distHelpers/mock_session.js +1 -1
  57. package/distHelpers/mock_session.js.map +1 -1
  58. package/package.json +35 -35
  59. package/source/address_space_ts.ts +0 -1
  60. package/source/helpers/argument_list.ts +13 -3
  61. package/source/helpers/multiform_func.ts +76 -0
  62. package/source/loader/load_nodeset2.ts +64 -80
  63. package/source/set_namespace_meta_data.ts +1 -1
  64. package/src/address_space.ts +16 -7
  65. package/src/alarms_and_conditions/condition_snapshot.ts +4 -4
  66. package/src/alarms_and_conditions/ua_acknowledgeable_condition_impl.ts +7 -6
  67. package/src/alarms_and_conditions/ua_alarm_condition_impl.ts +2 -2
  68. package/src/alarms_and_conditions/ua_condition_impl.ts +29 -15
  69. package/src/alarms_and_conditions/ua_off_normal_alarm_impl.ts +1 -1
  70. package/src/base_node_impl.ts +3 -1
  71. package/src/base_node_private.ts +282 -36
  72. package/src/event_data.ts +1 -1
  73. package/src/namespace_impl.ts +6 -6
  74. package/src/nodeset_tools/nodeset_to_xml.ts +20 -10
  75. package/src/nodeset_tools/typedictionary_to_xml.ts +17 -7
  76. package/src/reference_impl.ts +3 -3
  77. package/src/state_machine/ua_shelving_state_machine_ex.ts +32 -19
  78. package/src/ua_data_type_impl.ts +168 -61
  79. package/src/ua_method_impl.ts +21 -7
  80. package/src/ua_object_impl.ts +10 -2
  81. package/src/ua_variable_impl.ts +419 -325
  82. package/src/ua_variable_type_impl.ts +86 -52
  83. package/src/ua_view_impl.ts +1 -1
  84. package/test_helpers/add_event_generator_object.ts +4 -3
  85. package/test_helpers/mock_session.ts +1 -1
  86. package/test_helpers/test_fixtures/fixture_simple_statemachine_nodeset2.xml +18 -0
  87. package/test_helpers/test_fixtures/fixuture_nodeset_objects_with_some_methods.xml +9 -1
  88. package/test_helpers/test_fixtures/mini.Node.Set2.xml +22 -1
@@ -8,7 +8,19 @@
8
8
  // tslint:disable:max-line-length
9
9
  import * as chalk from "chalk";
10
10
 
11
- import { ContinuationData, GetFunc, SetFunc } from "node-opcua-address-space-base";
11
+ import {
12
+ CloneExtraInfo,
13
+ ContinuationData,
14
+ defaultCloneExtraInfo,
15
+ defaultCloneFilter,
16
+ GetFunc,
17
+ SetFunc,
18
+ VariableDataValueGetterCallback,
19
+ VariableDataValueGetterPromise,
20
+ VariableDataValueGetterSync,
21
+ VariableDataValueSetterWithCallback,
22
+ VariableDataValueSetterWithPromise
23
+ } from "node-opcua-address-space-base";
12
24
  import { assert } from "node-opcua-assert";
13
25
  import {
14
26
  isValidDataEncoding,
@@ -37,17 +49,25 @@ import {
37
49
  ReadProcessedDetails,
38
50
  ReadRawModifiedDetails,
39
51
  StructureDefinition,
52
+ StructureField,
40
53
  WriteValueOptions
41
54
  } from "node-opcua-types";
42
55
  import * as utils from "node-opcua-utils";
43
56
  import { lowerFirstLetter } from "node-opcua-utils";
44
- import { Variant, VariantLike, DataType, sameVariant, VariantArrayType, adjustVariant } from "node-opcua-variant";
57
+ import {
58
+ Variant,
59
+ VariantLike,
60
+ DataType,
61
+ sameVariant,
62
+ VariantArrayType,
63
+ adjustVariant,
64
+ verifyRankAndDimensions
65
+ } from "node-opcua-variant";
45
66
  import { StatusCodeCallback } from "node-opcua-status-code";
46
67
  import {
47
68
  IAddressSpace,
48
69
  BindVariableOptions,
49
70
  ContinuationPoint,
50
- DataValueCallback,
51
71
  IVariableHistorian,
52
72
  TimestampGetFunc,
53
73
  TimestampSetFunc,
@@ -56,7 +76,6 @@ import {
56
76
  UAVariableType,
57
77
  CloneOptions,
58
78
  CloneFilter,
59
- CloneExtraInfo,
60
79
  ISessionContext,
61
80
  BaseNode,
62
81
  UAVariableT
@@ -64,6 +83,7 @@ import {
64
83
  import { UAHistoricalDataConfiguration } from "node-opcua-nodeset-ua";
65
84
 
66
85
  import { SessionContext } from "../source/session_context";
86
+ import { convertToCallbackFunction1 } from "../source/helpers/multiform_func";
67
87
  import { BaseNodeImpl, InternalBaseNodeOptions } from "./base_node_impl";
68
88
  import { _clone, ToStringBuilder, UAVariable_toString, valueRankToString } from "./base_node_private";
69
89
  import { EnumerationInfo, IEnumItem, UADataTypeImpl } from "./ua_data_type_impl";
@@ -162,16 +182,16 @@ function validateDataType(
162
182
  if (variantDataType === DataType.Null && !allowNulls) {
163
183
  return false;
164
184
  }
165
- let builtInType: string;
185
+ let builtInType: DataType;
166
186
  let builtInUADataType: UADataType;
167
187
 
168
- const destUADataType = addressSpace.findNode(dataTypeNodeId) as UADataType;
188
+ const destUADataType = addressSpace.findDataType(dataTypeNodeId)!;
169
189
  assert(destUADataType instanceof UADataTypeImpl);
170
190
 
171
191
  if (destUADataType.isAbstract || destUADataType.nodeId.namespace !== 0) {
172
192
  builtInUADataType = destUADataType;
173
193
  } else {
174
- builtInType = DataType[addressSpace.findCorrespondingBasicDataType(destUADataType)];
194
+ builtInType = addressSpace.findCorrespondingBasicDataType(destUADataType);
175
195
  builtInUADataType = addressSpace.findDataType(builtInType)!;
176
196
  }
177
197
  assert(builtInUADataType instanceof UADataTypeImpl);
@@ -183,8 +203,8 @@ function validateDataType(
183
203
  if (destUADataType.isSupertypeOf(enumerationUADataType)) {
184
204
  // istanbul ignore next
185
205
  if (doDebug) {
186
- console.log("destUADataType.", destUADataType.browseName.toString(), destUADataType.nodeId.toString());
187
- console.log(
206
+ debugLog("destUADataType.", destUADataType.browseName.toString(), destUADataType.nodeId.toString());
207
+ debugLog(
188
208
  "enumerationUADataType.",
189
209
  enumerationUADataType.browseName.toString(),
190
210
  enumerationUADataType.nodeId.toString()
@@ -203,19 +223,23 @@ function validateDataType(
203
223
  if (doDebug) {
204
224
  if (dest_isSuperTypeOf_variant) {
205
225
  /* istanbul ignore next*/
206
- console.log(chalk.green(" ---------- Type match !!! "), " on ", nodeId.toString());
226
+ debugLog(chalk.green(" ---------- Type match !!! "), " on ", nodeId.toString());
207
227
  } else {
208
228
  /* istanbul ignore next*/
209
- console.log(chalk.red(" ---------- Type mismatch "), " on ", nodeId.toString());
229
+ debugLog(chalk.red(" ---------- Type mismatch "), " on ", nodeId.toString());
210
230
  }
211
- console.log(chalk.cyan(" Variable data Type is = "), destUADataType.browseName.toString());
212
- console.log(chalk.cyan(" which matches basic Type = "), builtInUADataType.browseName.toString());
213
- console.log(chalk.yellow(" Actual dataType = "), variantUADataType.browseName.toString());
231
+ debugLog(chalk.cyan(" Variable data Type is = "), destUADataType.browseName.toString());
232
+ debugLog(chalk.cyan(" which matches basic Type = "), builtInUADataType.browseName.toString());
233
+ debugLog(chalk.yellow(" Actual dataType = "), variantUADataType.browseName.toString());
214
234
  }
215
235
 
216
236
  return dest_isSuperTypeOf_variant;
217
237
  }
218
238
 
239
+ function default_func(this: UAVariable, dataValue1: DataValue, callback1: CallbackT<StatusCode>) {
240
+ return _default_writable_timestamped_set_func.call(this, dataValue1, callback1);
241
+ }
242
+
219
243
  interface UAVariableOptions extends InternalBaseNodeOptions {
220
244
  value?: any;
221
245
  dataType: NodeId | string;
@@ -227,50 +251,6 @@ interface UAVariableOptions extends InternalBaseNodeOptions {
227
251
  historizing?: number;
228
252
  }
229
253
 
230
- export function verifyRankAndDimensions(options: { valueRank?: number; arrayDimensions?: number[] | null }): void {
231
- // evaluate valueRank arrayDimensions is specified but valueRank is null
232
- if (options.arrayDimensions && options.valueRank === undefined) {
233
- options.valueRank = options.arrayDimensions.length;
234
- }
235
- options.valueRank = options.valueRank === undefined ? -1 : options.valueRank || 0; // UInt32
236
- assert(typeof options.valueRank === "number");
237
-
238
- options.arrayDimensions = options.arrayDimensions || null;
239
- assert(options.arrayDimensions === null || Array.isArray(options.arrayDimensions));
240
-
241
- if (options.arrayDimensions && options.valueRank <= 0) {
242
- throw new Error("[CONFORMANCE] arrayDimensions must be null if valueRank <=0");
243
- }
244
- // specify default arrayDimension if not provided
245
- if (options.valueRank > 0 && !options.arrayDimensions) {
246
- options.arrayDimensions = new Array(options.valueRank).fill(0);
247
- }
248
- if (!options.arrayDimensions && options.valueRank > 0) {
249
- throw new Error("[CONFORMANCE] arrayDimension must be specified if valueRank >0 " + options.valueRank);
250
- }
251
- if (options.valueRank > 0 && options.arrayDimensions!.length !== options.valueRank) {
252
- throw new Error(
253
- "[CONFORMANCE] when valueRank> 0, arrayDimensions must have valueRank elements, this.valueRank =" +
254
- options.valueRank +
255
- " whereas arrayDimensions.length =" +
256
- options.arrayDimensions!.length
257
- );
258
- }
259
- }
260
-
261
- type TimestampGetFunction1 = () => DataValue | Promise<DataValue>;
262
- type TimestampGetFunction2 = (callback: (err: Error | null, dataValue?: DataValue) => void) => void;
263
- type TimestampGetFunction = TimestampGetFunction1 | TimestampGetFunction2;
264
-
265
- type TimestampSetFunction1 = (this: UAVariable, dataValue: DataValue, indexRange: NumericRange) => void | Promise<void>;
266
- type TimestampSetFunction2 = (
267
- this: UAVariable,
268
- dataValue: DataValue,
269
- indexRange: NumericRange,
270
- callback: (err: Error | null, StatusCode: StatusCode) => void
271
- ) => void;
272
- type TimestampSetFunction = TimestampSetFunction1 | TimestampSetFunction2;
273
-
274
254
  /**
275
255
  * A OPCUA Variable Node
276
256
  *
@@ -299,7 +279,9 @@ type TimestampSetFunction = TimestampSetFunction1 | TimestampSetFunction2;
299
279
  */
300
280
  export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
301
281
  public readonly nodeClass = NodeClass.Variable;
282
+
302
283
  public dataType: NodeId;
284
+ private _basicDataType?: DataType;
303
285
 
304
286
  public $extensionObject?: any;
305
287
 
@@ -307,9 +289,9 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
307
289
  public varHistorian?: IVariableHistorian;
308
290
 
309
291
  /**
310
- * @internal
292
+ * @internal @private
311
293
  */
312
- public _dataValue: DataValue;
294
+ public $dataValue: DataValue;
313
295
  public accessLevel: number;
314
296
  public userAccessLevel?: number;
315
297
  public valueRank: number;
@@ -318,11 +300,11 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
318
300
  public semantic_version: number;
319
301
  public arrayDimensions: null | number[];
320
302
 
321
- public _timestamped_get_func?: TimestampGetFunction | null;
322
- public _timestamped_set_func?: TimestampSetFunction | null;
303
+ public _timestamped_get_func?: TimestampGetFunc | null;
304
+ public _timestamped_set_func?: VariableDataValueSetterWithCallback | null;
323
305
  public _get_func: any;
324
306
  public _set_func: any;
325
- public refreshFunc?: (callback: DataValueCallback) => void;
307
+ public refreshFunc?: (callback: CallbackT<DataValue>) => void;
326
308
  public __waiting_callbacks?: any[];
327
309
 
328
310
  get typeDefinitionObj(): UAVariableType {
@@ -346,11 +328,9 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
346
328
 
347
329
  this.minimumSamplingInterval = adjust_samplingInterval(options.minimumSamplingInterval || 0);
348
330
 
349
- this.historizing = !!options.historizing; // coerced to boolean
331
+ this.historizing = !!options.historizing; // coerced to boolean"
350
332
 
351
- this._dataValue = new DataValue({ statusCode: StatusCodes.UncertainInitialValue, value: {} });
352
-
353
- // xx options.value = options.value || { dataType: DataType.Null };
333
+ this.$dataValue = new DataValue({ statusCode: StatusCodes.UncertainInitialValue, value: { dataType: DataType.Null } });
354
334
 
355
335
  if (options.value) {
356
336
  this.bindVariable(options.value);
@@ -453,11 +433,17 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
453
433
 
454
434
  if (this._timestamped_get_func) {
455
435
  if (this._timestamped_get_func.length === 0) {
456
- this._dataValue = (this._timestamped_get_func as TimestampGetFunction1)() as DataValue;
436
+ const dataValueOrPromise = (this._timestamped_get_func as VariableDataValueGetterSync)();
437
+ if (!Object.prototype.hasOwnProperty.call(dataValueOrPromise, "then")) {
438
+ this.$dataValue = dataValueOrPromise as DataValue;
439
+ this.verifyVariantCompatibility(this.$dataValue.value);
440
+ } else {
441
+ errorLog("Unsupported: _timestamped_get_func returns a Promise !");
442
+ }
457
443
  }
458
444
  }
459
445
 
460
- let dataValue = this._dataValue;
446
+ let dataValue = this.$dataValue;
461
447
 
462
448
  if (isGoodish(dataValue.statusCode)) {
463
449
  // note : extractRange will clone the dataValue
@@ -504,12 +490,14 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
504
490
  return dataTypeNode._getEnumerationInfo();
505
491
  }
506
492
 
507
- public asyncRefresh(oldestDate: Date, callback: DataValueCallback): void;
493
+ public asyncRefresh(oldestDate: Date, callback: CallbackT<DataValue>): void;
508
494
  public asyncRefresh(oldestDate: Date): Promise<DataValue>;
509
495
  public asyncRefresh(...args: any[]): any {
496
+ this.verifyVariantCompatibility(this.$dataValue.value);
497
+
510
498
  const oldestDate = args[0] as Date;
511
499
  assert(oldestDate instanceof Date);
512
- const callback = args[1] as DataValueCallback;
500
+ const callback = args[1] as CallbackT<DataValue>;
513
501
 
514
502
  if (!this.refreshFunc) {
515
503
  // no refresh func
@@ -524,22 +512,36 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
524
512
  }
525
513
  }
526
514
 
527
- if (this._dataValue.serverTimestamp && oldestDate.getTime() <= this._dataValue.serverTimestamp!.getTime()) {
515
+ if (this.$dataValue.serverTimestamp && oldestDate.getTime() <= this.$dataValue.serverTimestamp!.getTime()) {
528
516
  const dataValue = this.readValue();
529
517
  dataValue.serverTimestamp = oldestDate;
530
518
  dataValue.serverPicoseconds = 0;
531
519
  return callback(null, dataValue);
532
520
  }
533
521
 
534
- this.refreshFunc.call(this, (err: Error | null, dataValue?: DataValueLike) => {
535
- if (err || !dataValue) {
536
- dataValue = { statusCode: StatusCodes.BadNoDataAvailable };
537
- }
538
- if (dataValue !== this._dataValue) {
539
- this._internal_set_dataValue(coerceDataValue(dataValue), null);
540
- }
541
- callback(err, this._dataValue);
542
- });
522
+ try {
523
+ this.refreshFunc.call(this, (err: Error | null, dataValue?: DataValueLike) => {
524
+ if (err || !dataValue) {
525
+ errorLog(
526
+ "-------------- refresh call failed",
527
+ this.browseName.toString(),
528
+ this.nodeId.toString(),
529
+ err?.message
530
+ );
531
+ dataValue = { statusCode: StatusCodes.BadNoDataAvailable };
532
+ }
533
+ if (dataValue !== this.$dataValue) {
534
+ this._internal_set_dataValue(coerceDataValue(dataValue), null);
535
+ }
536
+ callback(err, this.$dataValue);
537
+ });
538
+ } catch (err) {
539
+ errorLog("-------------- refresh call failed 2", this.browseName.toString(), this.nodeId.toString());
540
+ errorLog(err);
541
+ const dataValue = new DataValue({ statusCode: StatusCodes.BadInternalError });
542
+ this._internal_set_dataValue(dataValue, null);
543
+ callback(err as Error, this.$dataValue);
544
+ }
543
545
  }
544
546
 
545
547
  public readEnumValue(): IEnumItem {
@@ -629,10 +631,91 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
629
631
  }
630
632
  }
631
633
 
632
- public adjustVariant(variant: Variant): Variant {
634
+ public getBasicDataType(): DataType {
635
+ if (this._basicDataType) {
636
+ return this._basicDataType;
637
+ }
638
+ if (this.dataType.namespace === 0 && this.dataType.value === 0) {
639
+ return DataType.Null;
640
+ }
633
641
  const addressSpace = this.addressSpace;
634
- const dataTypeNodeId = addressSpace.findCorrespondingBasicDataType(this.dataType);
635
- return adjustVariant(variant, this.valueRank, dataTypeNodeId);
642
+ const dataTypeNode = addressSpace.findDataType(this.dataType)!;
643
+ const basicDataType = dataTypeNode ? dataTypeNode.getBasicDataType() : DataType.Null;
644
+ // const basicDataType = addressSpace.findCorrespondingBasicDataType(this.dataType);
645
+ this._basicDataType = basicDataType;
646
+ return basicDataType;
647
+ }
648
+ public adjustVariant(variant: Variant): Variant {
649
+ return adjustVariant(variant, this.valueRank, this.getBasicDataType());
650
+ }
651
+ public verifyVariantCompatibility(variant: Variant): void {
652
+ try {
653
+ // istanbul ignore next
654
+ if (Object.prototype.hasOwnProperty.call(variant, "value")) {
655
+ if (variant.dataType === null || variant.dataType === undefined) {
656
+ throw new Error(
657
+ "Variant must provide a valid dataType : variant = " +
658
+ variant.toString() +
659
+ " this.dataType= " +
660
+ this.dataType.toString()
661
+ );
662
+ }
663
+ if (
664
+ variant.dataType === DataType.Boolean &&
665
+ (this.dataType.namespace !== 0 || this.dataType.value !== DataType.Boolean)
666
+ ) {
667
+ throw new Error(
668
+ "Variant must provide a valid Boolean : variant = " +
669
+ variant.toString() +
670
+ " this.dataType= " +
671
+ this.dataType.toString()
672
+ );
673
+ }
674
+ if (
675
+ this.dataType.namespace === 0 &&
676
+ this.dataType.value === DataType.LocalizedText &&
677
+ variant.dataType !== DataType.LocalizedText
678
+ ) {
679
+ throw new Error(
680
+ "Variant must provide a valid LocalizedText : variant = " +
681
+ variant.toString() +
682
+ " this.dataType= " +
683
+ this.dataType.toString()
684
+ );
685
+ }
686
+ }
687
+ const basicType = this.getBasicDataType();
688
+ if (
689
+ basicType !== DataType.Null &&
690
+ basicType !== DataType.Variant &&
691
+ variant.dataType !== DataType.Null &&
692
+ variant.dataType !== basicType
693
+ ) {
694
+ const message =
695
+ "UAVariable.setValueFromSource " +
696
+ this.browseName.toString() +
697
+ " nodeId:" +
698
+ this.nodeId.toString() +
699
+ " dataType:" +
700
+ this.dataType.toString() +
701
+ ":\n" +
702
+ "the provided variant must have the expected dataType!\n" +
703
+ " - the expected dataType is " +
704
+ chalk.cyan(DataType[basicType]) +
705
+ "\n" +
706
+ " - the actual dataType is " +
707
+ chalk.magenta(DataType[variant.dataType]) +
708
+ "\n" +
709
+ " - " +
710
+ variant.toString();
711
+ throw new Error(message);
712
+ }
713
+ } catch (err) {
714
+ errorLog("UAVariable ", (err as Error)?.message, this.browseName.toString(), " nodeId=", this.nodeId.toString());
715
+ errorLog((err as Error).message);
716
+ errorLog((err as Error).stack);
717
+ throw err;
718
+ }
636
719
  }
637
720
  /**
638
721
  * setValueFromSource is used to let the device sets the variable values
@@ -646,39 +729,26 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
646
729
  * @param [sourceTimestamp= Now]
647
730
  */
648
731
  public setValueFromSource(variant: VariantLike, statusCode?: StatusCode, sourceTimestamp?: Date): void {
649
- statusCode = statusCode || StatusCodes.Good;
650
- // istanbul ignore next
651
- if (Object.prototype.hasOwnProperty.call(variant, "value")) {
652
- if (variant.dataType === null || variant.dataType === undefined) {
653
- throw new Error("Variant must provide a valid dataType" + variant.toString());
654
- }
655
- if (
656
- variant.dataType === DataType.Boolean &&
657
- (this.dataType.namespace !== 0 || this.dataType.value !== DataType.Boolean)
658
- ) {
659
- throw new Error("Variant must provide a valid Boolean" + variant.toString());
660
- }
661
- if (
662
- this.dataType.namespace === 0 &&
663
- this.dataType.value === DataType.LocalizedText &&
664
- variant.dataType !== DataType.LocalizedText
665
- ) {
666
- throw new Error("Variant must provide a valid LocalizedText" + variant.toString());
667
- }
732
+ try {
733
+ statusCode = statusCode || StatusCodes.Good;
734
+ const variant1 = Variant.coerce(variant);
735
+ this.verifyVariantCompatibility(variant1);
736
+ const now = coerceClock(sourceTimestamp, 0);
737
+
738
+ const dataValue = new DataValue(null);
739
+ dataValue.serverPicoseconds = now.picoseconds;
740
+ dataValue.serverTimestamp = now.timestamp;
741
+ dataValue.sourcePicoseconds = now.picoseconds;
742
+ dataValue.sourceTimestamp = now.timestamp;
743
+ dataValue.statusCode = statusCode;
744
+ dataValue.value = variant1;
745
+ this._internal_set_dataValue(dataValue);
746
+ } catch (err) {
747
+ errorLog("UAVariable#setValueFromString Error : ", this.browseName.toString(), this.nodeId.toString());
748
+ errorLog((err as Error).message);
749
+ errorLog(this.parent?.toString());
750
+ throw err;
668
751
  }
669
-
670
- const variant1 = Variant.coerce(variant);
671
-
672
- const now = coerceClock(sourceTimestamp, 0);
673
-
674
- const dataValue = new DataValue(null);
675
- dataValue.serverPicoseconds = now.picoseconds;
676
- dataValue.serverTimestamp = now.timestamp;
677
- dataValue.sourcePicoseconds = now.picoseconds;
678
- dataValue.sourceTimestamp = now.timestamp;
679
- dataValue.statusCode = statusCode;
680
- dataValue.value = variant1;
681
- this._internal_set_dataValue(dataValue);
682
752
  }
683
753
 
684
754
  public writeValue(
@@ -749,16 +819,7 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
749
819
  return callback!(null, statusCode);
750
820
  }
751
821
 
752
- function default_func(
753
- this: UAVariable,
754
- dataValue1: DataValue,
755
- indexRange1: NumericRange,
756
- callback1: (err: Error | null, statusCode: StatusCode, dataValue?: DataValue | null | undefined) => void
757
- ) {
758
- // xx assert(!indexRange,"indexRange Not Implemented");
759
- return _default_writable_timestamped_set_func.call(this, dataValue1, callback1);
760
- }
761
- const write_func = (this._timestamped_set_func || default_func) as any;
822
+ const write_func = this._timestamped_set_func || default_func;
762
823
 
763
824
  if (!write_func) {
764
825
  warningLog(" warning " + this.nodeId.toString() + " " + this.browseName.toString() + " has no setter. \n");
@@ -767,77 +828,70 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
767
828
  }
768
829
  assert(write_func);
769
830
 
770
- write_func.call(
771
- this,
772
- dataValue,
773
- indexRange,
774
- (err: Error | null, statusCode1?: StatusCode, correctedDataValue?: DataValue) => {
775
- if (!err) {
776
- correctedDataValue = correctedDataValue || dataValue;
777
- assert(correctedDataValue instanceof DataValue);
778
- // xx assert(correctedDataValue.serverTimestamp);
779
-
780
- if (indexRange && !indexRange.isEmpty()) {
781
- if (!indexRange.isValid()) {
782
- return callback!(null, StatusCodes.BadIndexRangeInvalid);
831
+ write_func.call(this, dataValue, (err?: Error | null, statusCode1?: StatusCode) => {
832
+ if (!err) {
833
+ dataValue && this.verifyVariantCompatibility(dataValue.value);
834
+
835
+ if (indexRange && !indexRange.isEmpty()) {
836
+ if (!indexRange.isValid()) {
837
+ return callback!(null, StatusCodes.BadIndexRangeInvalid);
838
+ }
839
+
840
+ const newArrayOrMatrix = dataValue.value.value;
841
+
842
+ if (dataValue.value.arrayType === VariantArrayType.Array) {
843
+ if (this.$dataValue.value.arrayType !== VariantArrayType.Array) {
844
+ return callback(null, StatusCodes.BadTypeMismatch);
783
845
  }
846
+ // check that destination data is also an array
847
+ assert(check_valid_array(this.$dataValue.value.dataType, this.$dataValue.value.value));
848
+ const destArr = this.$dataValue.value.value;
849
+ const result = indexRange.set_values(destArr, newArrayOrMatrix);
784
850
 
785
- const newArrayOrMatrix = correctedDataValue.value.value;
786
-
787
- if (correctedDataValue.value.arrayType === VariantArrayType.Array) {
788
- if (this._dataValue.value.arrayType !== VariantArrayType.Array) {
789
- return callback(null, StatusCodes.BadTypeMismatch);
790
- }
791
- // check that destination data is also an array
792
- assert(check_valid_array(this._dataValue.value.dataType, this._dataValue.value.value));
793
- const destArr = this._dataValue.value.value;
794
- const result = indexRange.set_values(destArr, newArrayOrMatrix);
795
-
796
- if (result.statusCode.isNot(StatusCodes.Good)) {
797
- return callback!(null, result.statusCode);
798
- }
799
- correctedDataValue.value.value = result.array;
800
-
801
- // scrap original array so we detect range
802
- this._dataValue.value.value = null;
803
- } else if (correctedDataValue.value.arrayType === VariantArrayType.Matrix) {
804
- const dimensions = this._dataValue.value.dimensions;
805
- if (this._dataValue.value.arrayType !== VariantArrayType.Matrix || !dimensions) {
806
- // not a matrix !
807
- return callback!(null, StatusCodes.BadTypeMismatch);
808
- }
809
- const matrix = this._dataValue.value.value;
810
- const result = indexRange.set_values_matrix(
811
- {
812
- matrix,
813
- dimensions
814
- },
815
- newArrayOrMatrix
816
- );
817
- if (result.statusCode.isNot(StatusCodes.Good)) {
818
- return callback!(null, result.statusCode);
819
- }
820
- correctedDataValue.value.dimensions = this._dataValue.value.dimensions;
821
- correctedDataValue.value.value = result.matrix;
822
-
823
- // scrap original array so we detect range
824
- this._dataValue.value.value = null;
825
- } else {
851
+ if (result.statusCode.isNot(StatusCodes.Good)) {
852
+ return callback!(null, result.statusCode);
853
+ }
854
+ dataValue.value.value = result.array;
855
+
856
+ // scrap original array so we detect range
857
+ this.$dataValue.value.value = null;
858
+ } else if (dataValue.value.arrayType === VariantArrayType.Matrix) {
859
+ const dimensions = this.$dataValue.value.dimensions;
860
+ if (this.$dataValue.value.arrayType !== VariantArrayType.Matrix || !dimensions) {
861
+ // not a matrix !
826
862
  return callback!(null, StatusCodes.BadTypeMismatch);
827
863
  }
828
- }
829
- try {
830
- this._internal_set_dataValue(correctedDataValue, indexRange);
831
- } catch (err) {
832
- if (err instanceof Error) {
833
- warningLog(err.message);
864
+ const matrix = this.$dataValue.value.value;
865
+ const result = indexRange.set_values_matrix(
866
+ {
867
+ matrix,
868
+ dimensions
869
+ },
870
+ newArrayOrMatrix
871
+ );
872
+ if (result.statusCode.isNot(StatusCodes.Good)) {
873
+ return callback!(null, result.statusCode);
834
874
  }
835
- return callback!(null, StatusCodes.BadInternalError);
875
+ dataValue.value.dimensions = this.$dataValue.value.dimensions;
876
+ dataValue.value.value = result.matrix;
877
+
878
+ // scrap original array so we detect range
879
+ this.$dataValue.value.value = null;
880
+ } else {
881
+ return callback!(null, StatusCodes.BadTypeMismatch);
882
+ }
883
+ }
884
+ try {
885
+ this._internal_set_dataValue(dataValue, indexRange);
886
+ } catch (err) {
887
+ if (err instanceof Error) {
888
+ warningLog(err.message);
836
889
  }
890
+ return callback!(null, StatusCodes.BadInternalError);
837
891
  }
838
- callback!(err, statusCode1);
839
892
  }
840
- );
893
+ callback!(err || null, statusCode1);
894
+ });
841
895
  }
842
896
 
843
897
  public writeAttribute(context: ISessionContext | null, writeValue: WriteValueOptions, callback: StatusCodeCallback): void;
@@ -903,6 +957,11 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
903
957
  if (!this._validate_DataType(value.dataType)) {
904
958
  return StatusCodes.BadTypeMismatch;
905
959
  }
960
+ try {
961
+ this.verifyVariantCompatibility(value);
962
+ } catch (err) {
963
+ return StatusCodes.BadTypeMismatch;
964
+ }
906
965
  return StatusCodes.Good;
907
966
  }
908
967
 
@@ -917,12 +976,12 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
917
976
  */
918
977
  public touchValue(optionalNow?: PreciseClock): void {
919
978
  const now = optionalNow || getCurrentClock();
920
- this._dataValue.sourceTimestamp = now.timestamp;
921
- this._dataValue.sourcePicoseconds = now.picoseconds;
922
- this._dataValue.serverTimestamp = now.timestamp;
923
- this._dataValue.serverPicoseconds = now.picoseconds;
979
+ this.$dataValue.sourceTimestamp = now.timestamp;
980
+ this.$dataValue.sourcePicoseconds = now.picoseconds;
981
+ this.$dataValue.serverTimestamp = now.timestamp;
982
+ this.$dataValue.serverPicoseconds = now.picoseconds;
924
983
 
925
- this._dataValue.statusCode = StatusCodes.Good;
984
+ this.$dataValue.statusCode = StatusCodes.Good;
926
985
 
927
986
  if (this.minimumSamplingInterval === 0) {
928
987
  if (this.listenerCount("value_changed") > 0) {
@@ -1070,6 +1129,7 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
1070
1129
 
1071
1130
  assert(typeof this._timestamped_set_func !== "function", "UAVariable already bound");
1072
1131
  assert(typeof this._timestamped_get_func !== "function", "UAVariable already bound");
1132
+
1073
1133
  bind_getter.call(this, options);
1074
1134
  bind_setter.call(this, options);
1075
1135
 
@@ -1081,9 +1141,9 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
1081
1141
  this._historyRead = _historyRead;
1082
1142
  assert(this._historyRead.length === 6);
1083
1143
  }
1084
-
1144
+ // post conditions
1085
1145
  assert(typeof this._timestamped_set_func === "function");
1086
- assert(this._timestamped_set_func!.length === 3);
1146
+ assert(this._timestamped_set_func!.length === 2, "expecting 2 parameters");
1087
1147
  }
1088
1148
 
1089
1149
  /**
@@ -1106,7 +1166,7 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
1106
1166
  }
1107
1167
 
1108
1168
  const readImmediate = (innerCallback: (err: Error | null, dataValue: DataValue) => void) => {
1109
- assert(this._dataValue instanceof DataValue);
1169
+ assert(this.$dataValue instanceof DataValue);
1110
1170
  const dataValue = this.readValue();
1111
1171
  innerCallback(null, dataValue);
1112
1172
  };
@@ -1174,14 +1234,20 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
1174
1234
  valueRank: this.valueRank
1175
1235
  };
1176
1236
 
1177
- const newVariable = _clone.call(this, UAVariableImpl, options, optionalFilter, extraInfo) as UAVariableImpl;
1237
+ const newVariable = _clone.call(
1238
+ this,
1239
+ UAVariableImpl,
1240
+ options,
1241
+ optionalFilter || defaultCloneFilter,
1242
+ extraInfo || defaultCloneExtraInfo
1243
+ ) as UAVariableImpl;
1178
1244
 
1179
1245
  newVariable.bindVariable();
1180
1246
 
1181
1247
  assert(typeof newVariable._timestamped_set_func === "function");
1182
1248
 
1183
1249
  assert(newVariable.dataType === this.dataType);
1184
- newVariable._dataValue = this._dataValue.clone();
1250
+ newVariable.$dataValue = this.$dataValue.clone();
1185
1251
  return newVariable;
1186
1252
  }
1187
1253
 
@@ -1204,8 +1270,10 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
1204
1270
  return true;
1205
1271
  }
1206
1272
  const addressSpace = this.addressSpace;
1273
+
1274
+ // istanbul ignore next
1207
1275
  if (!(extObj && extObj.constructor)) {
1208
- console.log(extObj);
1276
+ errorLog(extObj);
1209
1277
  throw new Error("expecting an valid extension object");
1210
1278
  }
1211
1279
  const dataType = addressSpace.findDataType(this.dataType);
@@ -1231,7 +1299,7 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
1231
1299
  return extObj.constructor.name === Constructor.name;
1232
1300
  }
1233
1301
  } catch (err) {
1234
- console.log(err);
1302
+ errorLog(err);
1235
1303
  return false;
1236
1304
  }
1237
1305
  }
@@ -1252,7 +1320,7 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
1252
1320
 
1253
1321
  // istanbul ignore next
1254
1322
  if (doDebug) {
1255
- console.log(" ------------------------------ binding ", this.browseName.toString(), this.nodeId.toString());
1323
+ debugLog(" ------------------------------ binding ", this.browseName.toString(), this.nodeId.toString());
1256
1324
  }
1257
1325
  assert(structure && structure.browseName.toString() === "Structure", "expecting DataType Structure to be in IAddressSpace");
1258
1326
 
@@ -1277,14 +1345,14 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
1277
1345
  // }
1278
1346
  // istanbul ignore next
1279
1347
  if (!this.checkExtensionObjectIsCorrect(this.$extensionObject!)) {
1280
- console.log(
1348
+ warningLog(
1281
1349
  "on node : ",
1282
1350
  this.browseName.toString(),
1283
1351
  this.nodeId.toString(),
1284
1352
  "dataType=",
1285
1353
  this.dataType.toString({ addressSpace: this.addressSpace })
1286
1354
  );
1287
- console.log(this.$extensionObject?.toString());
1355
+ warningLog(this.$extensionObject?.toString());
1288
1356
  throw new Error(
1289
1357
  "bindExtensionObject: $extensionObject is incorrect: we are expecting a " +
1290
1358
  this.dataType.toString({ addressSpace: this.addressSpace }) +
@@ -1299,45 +1367,32 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
1299
1367
 
1300
1368
  // ------------------------------------------------------------------
1301
1369
 
1302
- function prepareVariantValue(dataType: DataType | string, value: VariantLike): VariantLike {
1303
- if (typeof dataType === "string") {
1304
- dataType = (DataType as any)[dataType];
1305
- }
1370
+ function prepareVariantValue(dataType: DataType, value: VariantLike): VariantLike {
1306
1371
  if ((dataType === DataType.Int32 || dataType === DataType.UInt32) && value && (value as any).key) {
1307
1372
  value = value.value;
1308
1373
  }
1309
1374
  return value;
1310
1375
  }
1311
1376
 
1312
- const bindProperty = (propertyNode: UAVariableImpl, name: any, extensionObject: any, dataTypeNodeId: any) => {
1313
- const dataTypeAsString = DataType[dataTypeNodeId];
1314
-
1315
- /*
1316
- property.setValueFromSource(new Variant({
1317
- dataType: dataType,
1318
- value: prepareVariantValue(dataType, this.$extensionObject[name])
1319
- }));
1320
- */
1321
-
1322
- assert(propertyNode.readValue().statusCode.equals(StatusCodes.Good));
1323
-
1377
+ const bindProperty = (propertyNode: UAVariableImpl, name: string, extensionObject: ExtensionObject, dataType: DataType) => {
1324
1378
  // eslint-disable-next-line @typescript-eslint/no-this-alias
1325
1379
  const self = this;
1326
1380
  propertyNode.bindVariable(
1327
1381
  {
1328
1382
  timestamped_get: () => {
1329
- const prop = self.$extensionObject[name];
1383
+ const propertyValue = self.$extensionObject[name];
1330
1384
 
1331
- if (prop === undefined) {
1332
- propertyNode._dataValue.value.dataType = DataType.Null;
1333
- propertyNode._dataValue.statusCode = StatusCodes.Good;
1334
- propertyNode._dataValue.value.value = null;
1335
- return new DataValue(propertyNode._dataValue);
1385
+ if (propertyValue === undefined) {
1386
+ propertyNode.$dataValue.value.dataType = DataType.Null;
1387
+ propertyNode.$dataValue.statusCode = StatusCodes.Good;
1388
+ propertyNode.$dataValue.value.value = null;
1389
+ return new DataValue(propertyNode.$dataValue);
1336
1390
  }
1337
- const value = prepareVariantValue(dataTypeNodeId, prop);
1338
- propertyNode._dataValue.statusCode = StatusCodes.Good;
1339
- propertyNode._dataValue.value.value = value;
1340
- return new DataValue(propertyNode._dataValue);
1391
+ const value = prepareVariantValue(dataType, propertyValue);
1392
+ propertyNode.$dataValue.statusCode = StatusCodes.Good;
1393
+ propertyNode.$dataValue.value.dataType = dataType;
1394
+ propertyNode.$dataValue.value.value = value;
1395
+ return new DataValue(propertyNode.$dataValue);
1341
1396
  },
1342
1397
  timestamped_set: (dataValue: DataValue, callback: CallbackT<StatusCode>) => {
1343
1398
  dataValue;
@@ -1355,8 +1410,8 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
1355
1410
  const s = this.readValue();
1356
1411
  // istanbul ignore next
1357
1412
  if (this.dataTypeObj.isAbstract) {
1358
- console.log("Warning the DataType associated with this Variable is abstract ", this.dataTypeObj.browseName.toString());
1359
- console.log("You need to provide a extension object yourself ");
1413
+ warningLog("Warning the DataType associated with this Variable is abstract ", this.dataTypeObj.browseName.toString());
1414
+ warningLog("You need to provide a extension object yourself ");
1360
1415
  throw new Error("bindExtensionObject requires a extensionObject as associated dataType is only abstract");
1361
1416
  }
1362
1417
  if (s.value && (s.value.dataType === DataType.Null || (s.value.dataType === DataType.ExtensionObject && !s.value.value))) {
@@ -1376,8 +1431,8 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
1376
1431
  this.bindVariable(
1377
1432
  {
1378
1433
  timestamped_get() {
1379
- self._dataValue.value.value = self.$extensionObject;
1380
- const d = new DataValue(self._dataValue);
1434
+ self.$dataValue.value.value = self.$extensionObject;
1435
+ const d = new DataValue(self.$dataValue);
1381
1436
  d.value = new Variant(d.value);
1382
1437
  return d;
1383
1438
  },
@@ -1386,7 +1441,6 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
1386
1441
  if (!self.checkExtensionObjectIsCorrect(ext)) {
1387
1442
  return callback(null, StatusCodes.BadInvalidArgument);
1388
1443
  }
1389
-
1390
1444
  self.$extensionObject = new Proxy(ext, makeHandler(self));
1391
1445
  self.touchValue();
1392
1446
  callback(null, StatusCodes.Good);
@@ -1402,51 +1456,65 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
1402
1456
  assert(s.statusCode.equals(StatusCodes.Good));
1403
1457
  }
1404
1458
 
1405
- let property: any;
1406
- let camelCaseName: any;
1407
1459
  // ------------------------------------------------------
1408
1460
  // now bind each member
1409
1461
  // ------------------------------------------------------
1410
- const definition = dt._getDefinition(false) as StructureDefinition | null;
1462
+ const definition = dt._getDefinition() as StructureDefinition | null;
1411
1463
 
1412
1464
  // istanbul ignore next
1413
1465
  if (!definition) {
1414
1466
  throw new Error("xx definition missing in " + dt.toString());
1415
1467
  }
1416
- for (const field of definition.fields || []) {
1417
- camelCaseName = lowerFirstLetter(field.name!);
1418
- const component = components.filter((f) => f.browseName.name!.toString() === field.name);
1419
- if (component.length === 1) {
1420
- property = component[0];
1468
+
1469
+ const getOrCreateProperty = (field: StructureField): UAVariableImpl => {
1470
+ let property: UAVariableImpl;
1471
+ const selectedComponents = components.filter(
1472
+ (f) => f instanceof UAVariableImpl && f.browseName.name!.toString() === field.name
1473
+ );
1474
+
1475
+ if (field.dataType.value === DataType.Variant) {
1476
+ warningLog("Warning : variant is not supported in ExtensionObject");
1477
+ }
1478
+ if (selectedComponents.length === 1) {
1479
+ property = selectedComponents[0] as UAVariableImpl;
1421
1480
  /* istanbul ignore next */
1422
1481
  } else {
1482
+ debugLog("adding missing array variable", field.name, this.browseName.toString(), this.nodeId.toString());
1423
1483
  // todo: Handle array appropriately...
1424
- assert(component.length === 0);
1484
+ assert(selectedComponents.length === 0);
1425
1485
  // create a variable (Note we may use ns=1;s=parentName/0:PropertyName)
1426
1486
  property = this.namespace.addVariable({
1427
1487
  browseName: { namespaceIndex: structureNamespace, name: field.name!.toString() },
1428
1488
  componentOf: this,
1429
1489
  dataType: field.dataType,
1430
1490
  minimumSamplingInterval: this.minimumSamplingInterval
1431
- });
1491
+ }) as UAVariableImpl;
1432
1492
  assert(property.minimumSamplingInterval === this.minimumSamplingInterval);
1433
1493
  }
1494
+ return property;
1495
+ };
1434
1496
 
1435
- property._dataValue.statusCode = StatusCodes.Good;
1436
- property.touchValue();
1437
-
1497
+ for (const field of definition.fields || []) {
1438
1498
  if (NodeId.sameNodeId(NodeId.nullNodeId, field.dataType)) {
1439
- debugLog("field.dataType is null ! " + field.name + " " + field.description?.text);
1440
- debugLog(" dataType replaced with BaseDataType ");
1499
+ warningLog("field.dataType is null ! ", field.toString(), NodeId.nullNodeId.toString());
1500
+ warningLog(" dataType replaced with BaseDataType ");
1501
+ warningLog(definition.toString());
1441
1502
  field.dataType = this.resolveNodeId("BaseDataType");
1442
1503
  }
1443
- const dataTypeNodeId = addressSpace.findCorrespondingBasicDataType(field.dataType);
1504
+
1505
+ const camelCaseName = lowerFirstLetter(field.name!);
1444
1506
  assert(Object.prototype.hasOwnProperty.call(this.$extensionObject, camelCaseName));
1445
1507
 
1508
+ const propertyNode = getOrCreateProperty(field);
1509
+
1510
+ propertyNode.$dataValue.statusCode = StatusCodes.Good;
1511
+ propertyNode.touchValue();
1512
+
1513
+ const basicDataType = addressSpace.findCorrespondingBasicDataType(field.dataType);
1514
+
1446
1515
  // istanbul ignore next
1447
1516
  if (doDebug) {
1448
1517
  const x = addressSpace.findNode(field.dataType)!.browseName.toString();
1449
- const basicType = addressSpace.findCorrespondingBasicDataType(field.dataType);
1450
1518
  debugLog(
1451
1519
  chalk.cyan("xxx"),
1452
1520
  " dataType",
@@ -1456,52 +1524,56 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
1456
1524
  chalk.cyan(w(valueRankToString(field.valueRank), 10)),
1457
1525
  chalk.green(w(x, 25)),
1458
1526
  "basicType = ",
1459
- chalk.yellow(w(basicType.toString(), 20)),
1460
- property.nodeId.toString(),
1461
- property.readValue().statusCode.toString()
1527
+ chalk.yellow(w(basicDataType.toString(), 20)),
1528
+ propertyNode.nodeId.toString(),
1529
+ propertyNode.readValue().statusCode.toString()
1462
1530
  );
1463
1531
  }
1464
- if (this.$extensionObject[camelCaseName] !== undefined && dataTypeNodeId === DataType.ExtensionObject) {
1532
+ if (this.$extensionObject[camelCaseName] !== undefined && basicDataType === DataType.ExtensionObject) {
1465
1533
  assert(this.$extensionObject[camelCaseName] instanceof Object);
1466
- this.$extensionObject[camelCaseName] = new Proxy(this.$extensionObject[camelCaseName], makeHandler(property));
1467
- property._dataValue.value = new Variant({
1468
- dataType: DataType.ExtensionObject,
1469
- value: this.$extensionObject[camelCaseName]
1470
- });
1471
- property.bindExtensionObject();
1472
- property.$extensionObject = this.$extensionObject[camelCaseName];
1534
+ this.$extensionObject[camelCaseName] = new Proxy(this.$extensionObject[camelCaseName], makeHandler(propertyNode));
1535
+
1536
+ propertyNode._internal_set_value(
1537
+ new Variant({
1538
+ dataType: DataType.ExtensionObject,
1539
+ value: this.$extensionObject[camelCaseName]
1540
+ })
1541
+ );
1542
+
1543
+ propertyNode.bindExtensionObject();
1544
+ propertyNode.$extensionObject = this.$extensionObject[camelCaseName];
1473
1545
  } else {
1474
- const dataTypeAsString = DataType[dataTypeNodeId];
1475
- assert(typeof dataTypeAsString === "string");
1476
1546
  const prop = this.$extensionObject[camelCaseName];
1477
-
1478
1547
  if (prop === undefined) {
1479
- property._dataValue.value = new Variant({
1480
- dataType: DataType.Null
1481
- });
1548
+ propertyNode._internal_set_value(
1549
+ new Variant({
1550
+ dataType: DataType.Null
1551
+ })
1552
+ );
1482
1553
  } else {
1483
- const preparedValue = prepareVariantValue(dataTypeNodeId, prop);
1484
- property._dataValue.value = new Variant({
1485
- dataType: dataTypeAsString,
1486
- value: preparedValue
1487
- });
1554
+ const preparedValue = prepareVariantValue(basicDataType, prop);
1555
+ propertyNode._internal_set_value(
1556
+ new Variant({
1557
+ dataType: basicDataType,
1558
+ value: preparedValue
1559
+ })
1560
+ );
1488
1561
  }
1489
1562
 
1490
1563
  // eslint-disable-next-line @typescript-eslint/no-this-alias
1491
1564
  const self = this;
1492
- property.camelCaseName = camelCaseName;
1493
- property.setValueFromSource = function (this: any, variant: VariantLike) {
1565
+ // property.camelCaseName = camelCaseName;
1566
+ propertyNode.setValueFromSource = function (this: UAVariableImpl, variant: VariantLike) {
1494
1567
  // eslint-disable-next-line @typescript-eslint/no-this-alias
1495
1568
  const inner_this = this;
1496
- variant = Variant.coerce(variant);
1497
- // xx console.log("PropertySetValueFromSource this", inner_this.nodeId.toString(), inner_this.browseName.toString(), variant.toString(), inner_this.dataType.toString());
1498
- // xx assert(variant.dataType === this.dataType);
1499
- self.$extensionObject[inner_this.camelCaseName] = variant.value;
1569
+ const variant1 = Variant.coerce(variant);
1570
+ inner_this.verifyVariantCompatibility(variant1);
1571
+ self.$extensionObject[camelCaseName] = variant1.value;
1500
1572
  self.touchValue();
1501
1573
  };
1502
1574
  }
1503
- assert(property.readValue().statusCode.equals(StatusCodes.Good));
1504
- bindProperty(property, camelCaseName, this.$extensionObject, dataTypeNodeId);
1575
+ assert(propertyNode.readValue().statusCode.equals(StatusCodes.Good));
1576
+ bindProperty(propertyNode, camelCaseName, this.$extensionObject, basicDataType);
1505
1577
  }
1506
1578
  assert(this.$extensionObject instanceof Object);
1507
1579
  return this.$extensionObject;
@@ -1543,7 +1615,6 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
1543
1615
  c1[name] = c2[name];
1544
1616
  c1[name] += 1;
1545
1617
 
1546
- // xx console.log(partialData);
1547
1618
  setExtensionObjectValue(this, partialData);
1548
1619
  }
1549
1620
 
@@ -1687,10 +1758,16 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
1687
1758
  return validateDataType(this.addressSpace, this.dataType, variantDataType, this.nodeId, /* allow Nulls */ false);
1688
1759
  }
1689
1760
 
1761
+ public _internal_set_value(value: Variant): void {
1762
+ if (value.dataType !== DataType.Null) {
1763
+ this.verifyVariantCompatibility(value);
1764
+ }
1765
+ this.$dataValue.value = value;
1766
+ }
1690
1767
  public _internal_set_dataValue(dataValue: DataValue, indexRange?: NumericRange | null): void {
1691
1768
  assert(dataValue, "expecting a dataValue");
1692
1769
  assert(dataValue instanceof DataValue, "expecting dataValue to be a DataValue");
1693
- assert(dataValue !== this._dataValue, "expecting dataValue to be different from previous DataValue instance");
1770
+ assert(dataValue !== this.$dataValue, "expecting dataValue to be different from previous DataValue instance");
1694
1771
 
1695
1772
  if (dataValue.value.arrayType === VariantArrayType.Matrix) {
1696
1773
  if (!dataValue.value.dimensions) {
@@ -1714,27 +1791,31 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
1714
1791
  // istanbul ignore next
1715
1792
  if (this.dataType.namespace === 0) {
1716
1793
  if (this.dataType.value === DataType.LocalizedText && dataValue.value.dataType !== DataType.LocalizedText) {
1717
- throw new Error("Invalid dataValue provided (expecting a LocalizedText) but got " + dataValue.toString());
1794
+ const message = "Invalid dataValue provided (expecting a LocalizedText) but got " + dataValue.toString();
1795
+ errorLog(message);
1796
+ throw new Error(message);
1718
1797
  }
1719
1798
  }
1720
1799
 
1721
- const old_dataValue = this._dataValue;
1800
+ this.verifyVariantCompatibility(dataValue.value);
1722
1801
 
1723
- this._dataValue = dataValue;
1724
- this._dataValue.statusCode = this._dataValue.statusCode || StatusCodes.Good;
1802
+ const old_dataValue = this.$dataValue;
1803
+
1804
+ this.$dataValue = dataValue;
1805
+ this.$dataValue.statusCode = this.$dataValue.statusCode || StatusCodes.Good;
1725
1806
 
1726
1807
  // repair missing timestamps
1727
1808
  if (!dataValue.serverTimestamp) {
1728
- this._dataValue.serverTimestamp = old_dataValue.serverTimestamp;
1729
- this._dataValue.serverPicoseconds = old_dataValue.serverPicoseconds;
1809
+ this.$dataValue.serverTimestamp = old_dataValue.serverTimestamp;
1810
+ this.$dataValue.serverPicoseconds = old_dataValue.serverPicoseconds;
1730
1811
  }
1731
1812
  if (!dataValue.sourceTimestamp) {
1732
- this._dataValue.sourceTimestamp = old_dataValue.sourceTimestamp;
1733
- this._dataValue.sourcePicoseconds = old_dataValue.sourcePicoseconds;
1813
+ this.$dataValue.sourceTimestamp = old_dataValue.sourceTimestamp;
1814
+ this.$dataValue.sourcePicoseconds = old_dataValue.sourcePicoseconds;
1734
1815
  }
1735
1816
 
1736
1817
  if (!sameDataValue(old_dataValue, dataValue)) {
1737
- this.emit("value_changed", this._dataValue, indexRange);
1818
+ this.emit("value_changed", this.$dataValue, indexRange);
1738
1819
  }
1739
1820
  }
1740
1821
 
@@ -1930,10 +2011,10 @@ function _calculateEffectiveUserAccessLevelFromPermission(
1930
2011
  }
1931
2012
  }
1932
2013
 
1933
- function adjustVariant2(this: UAVariable, variant: Variant): Variant {
2014
+ function adjustVariant2(this: UAVariableImpl, variant: Variant): Variant {
1934
2015
  // convert Variant( Scalar|ByteString) => Variant(Array|ByteArray)
1935
2016
  const addressSpace = this.addressSpace;
1936
- const basicType = addressSpace.findCorrespondingBasicDataType(this.dataType);
2017
+ const basicType = this.getBasicDataType();
1937
2018
  variant = adjustVariant(variant, this.valueRank, basicType);
1938
2019
  return variant;
1939
2020
  }
@@ -1950,7 +2031,6 @@ function _default_writable_timestamped_set_func(
1950
2031
  dataValue: DataValue,
1951
2032
  callback: (err: Error | null, statusCode: StatusCode, dataValue?: DataValue | null) => void
1952
2033
  ) {
1953
- /* jshint validthis: true */
1954
2034
  assert(dataValue instanceof DataValue);
1955
2035
  callback(null, StatusCodes.Good, dataValue);
1956
2036
  }
@@ -1992,7 +2072,7 @@ function _Variable_bind_with_async_refresh(this: UAVariableImpl, options: any) {
1992
2072
  this.refreshFunc = options.refreshFunc;
1993
2073
 
1994
2074
  // assert(this.readValue().statusCode === StatusCodes.BadNodeIdUnknown);
1995
- this._dataValue.statusCode = StatusCodes.UncertainInitialValue;
2075
+ this.$dataValue.statusCode = StatusCodes.UncertainInitialValue;
1996
2076
 
1997
2077
  // TO DO : REVISIT THIS ASSUMPTION
1998
2078
  if (false && this.minimumSamplingInterval === 0) {
@@ -2004,7 +2084,13 @@ function _Variable_bind_with_async_refresh(this: UAVariableImpl, options: any) {
2004
2084
  }
2005
2085
 
2006
2086
  // variation 2
2007
- function _Variable_bind_with_timestamped_get(this: UAVariableImpl, options: any) {
2087
+ function _Variable_bind_with_timestamped_get(
2088
+ this: UAVariableImpl,
2089
+ options: {
2090
+ get: undefined;
2091
+ timestamped_get: TimestampGetFunc;
2092
+ }
2093
+ ) {
2008
2094
  /* jshint validthis: true */
2009
2095
  assert(this instanceof UAVariableImpl);
2010
2096
  assert(typeof options.timestamped_get === "function");
@@ -2012,17 +2098,20 @@ function _Variable_bind_with_timestamped_get(this: UAVariableImpl, options: any)
2012
2098
  assert(!this._timestamped_get_func);
2013
2099
 
2014
2100
  const async_refresh_func = (callback: (err: Error | null, dataValue?: DataValue) => void) => {
2015
- Promise.resolve((this._timestamped_get_func! as TimestampGetFunction1).call(this))
2101
+ Promise.resolve((this._timestamped_get_func! as VariableDataValueGetterSync).call(this))
2016
2102
  .then((dataValue) => callback(null, dataValue))
2017
- .catch((err) => callback(err as Error));
2103
+ .catch((err) => {
2104
+ errorLog("asyncRefresh error: Variable is ", this.nodeId.toString(), this.browseName.toString());
2105
+ callback(err as Error);
2106
+ });
2018
2107
  };
2019
-
2108
+ const pThis = this as UAVariable;
2020
2109
  if (options.timestamped_get.length === 0) {
2021
- const timestamped_get = options.timestamped_get as TimestampGetFunction1;
2110
+ const timestamped_get = options.timestamped_get as (this: UAVariable) => DataValue | Promise<DataValue>;
2022
2111
  // sync version | Promise version
2023
2112
  this._timestamped_get_func = timestamped_get;
2024
2113
 
2025
- const dataValue_verify = timestamped_get!.call(this);
2114
+ const dataValue_verify = timestamped_get.call(pThis);
2026
2115
  // dataValue_verify should be a DataValue or a Promise
2027
2116
  /* istanbul ignore next */
2028
2117
  if (!(dataValue_verify instanceof DataValue) && typeof dataValue_verify.then !== "function") {
@@ -2045,11 +2134,11 @@ function _Variable_bind_with_timestamped_get(this: UAVariableImpl, options: any)
2045
2134
  }
2046
2135
 
2047
2136
  // variation 1
2048
- function _Variable_bind_with_simple_get(this: UAVariableImpl, options: any) {
2137
+ function _Variable_bind_with_simple_get(this: UAVariableImpl, options: GetterOptions) {
2049
2138
  /* jshint validthis: true */
2050
2139
  assert(this instanceof UAVariableImpl);
2051
2140
  assert(typeof options.get === "function", "should specify get function");
2052
- assert(options.get.length === 0, "get function should not have arguments");
2141
+ assert(options.get!.length === 0, "get function should not have arguments");
2053
2142
  assert(!options.timestamped_get, "should not specify a timestamped_get function when get is specified");
2054
2143
  assert(!this._timestamped_get_func);
2055
2144
  assert(!this._get_func);
@@ -2073,16 +2162,15 @@ function _Variable_bind_with_simple_get(this: UAVariableImpl, options: any) {
2073
2162
  if (is_StatusCode(value)) {
2074
2163
  return new DataValue({ statusCode: value });
2075
2164
  } else {
2076
- if (!this._dataValue || !isGoodish(this._dataValue.statusCode) || !sameVariant(this._dataValue.value, value)) {
2165
+ if (!this.$dataValue || !isGoodish(this.$dataValue.statusCode) || !sameVariant(this.$dataValue.value, value)) {
2077
2166
  this.setValueFromSource(value, StatusCodes.Good);
2078
- } else {
2079
- // XX console.log("YYYYYYYYYYYYYYYYYYYYYYYYYY",this.browseName.toString());
2080
2167
  }
2081
- return this._dataValue;
2168
+ return this.$dataValue;
2082
2169
  }
2083
2170
  };
2084
2171
 
2085
2172
  _Variable_bind_with_timestamped_get.call(this, {
2173
+ get: undefined,
2086
2174
  timestamped_get: timestamped_get_func_from__Variable_bind_with_simple_get
2087
2175
  });
2088
2176
  }
@@ -2100,16 +2188,16 @@ function _Variable_bind_with_simple_set(this: UAVariableImpl, options: any) {
2100
2188
 
2101
2189
  this._timestamped_set_func = (
2102
2190
  timestamped_value: DataValue,
2103
- indexRange: NumericRange,
2104
2191
  callback: (err: Error | null, statusCode: StatusCode, dataValue: DataValue) => void
2105
2192
  ) => {
2106
2193
  assert(timestamped_value instanceof DataValue);
2107
2194
  this._set_func(timestamped_value.value, (err: Error | null, statusCode: StatusCode) => {
2195
+ // istanbul ignore next
2108
2196
  if (!err && !statusCode) {
2109
- console.log(
2197
+ errorLog(
2110
2198
  chalk.red("UAVariable Binding Error _set_func must return a StatusCode, check the bindVariable parameters")
2111
2199
  );
2112
- console.log(chalk.yellow("StatusCode.Good is assumed"));
2200
+ errorLog(chalk.yellow("StatusCode.Good is assumed"));
2113
2201
  return callback(err, StatusCodes.Good, timestamped_value);
2114
2202
  }
2115
2203
  callback(err, statusCode, timestamped_value);
@@ -2117,22 +2205,20 @@ function _Variable_bind_with_simple_set(this: UAVariableImpl, options: any) {
2117
2205
  };
2118
2206
  }
2119
2207
 
2120
- function _Variable_bind_with_timestamped_set(this: UAVariableImpl, options: any) {
2121
- assert(this instanceof UAVariableImpl);
2208
+ function _Variable_bind_with_timestamped_set(
2209
+ this: UAVariableImpl,
2210
+ options: {
2211
+ timestamped_set: TimestampSetFunc;
2212
+ set: undefined;
2213
+ }
2214
+ ) {
2122
2215
  assert(typeof options.timestamped_set === "function");
2123
2216
  assert(
2124
2217
  options.timestamped_set.length === 2,
2125
2218
  "timestamped_set must have 2 parameters timestamped_set: function(dataValue,callback){}"
2126
2219
  );
2127
2220
  assert(!options.set, "should not specify set when timestamped_set_func exists ");
2128
- this._timestamped_set_func = (
2129
- dataValue: DataValue,
2130
- indexRange: NumericRange,
2131
- callback: (err: Error | null, statusCode: StatusCode, dataValue: DataValue) => void
2132
- ) => {
2133
- // xx assert(!indexRange,"indexRange Not Implemented");
2134
- return options.timestamped_set.call(this, dataValue, callback);
2135
- };
2221
+ this._timestamped_set_func = convertToCallbackFunction1<StatusCode, DataValue, UAVariable>(options.timestamped_set);
2136
2222
  }
2137
2223
 
2138
2224
  interface SetterOptions {
@@ -2147,15 +2233,20 @@ function bind_setter(this: UAVariableImpl, options: SetterOptions) {
2147
2233
  } else if (typeof options.timestamped_set === "function") {
2148
2234
  // variation 2
2149
2235
  assert(typeof options.timestamped_get === "function", "timestamped_set must be used with timestamped_get ");
2150
- _Variable_bind_with_timestamped_set.call(this, options);
2236
+ _Variable_bind_with_timestamped_set.call(this, {
2237
+ set: undefined,
2238
+ timestamped_set: options.timestamped_set
2239
+ });
2151
2240
  } else if (typeof options.timestamped_get === "function") {
2152
2241
  // timestamped_get is specified but timestamped_set is not
2153
2242
  // => Value is read-only
2154
2243
  _Variable_bind_with_timestamped_set.call(this, {
2244
+ set: undefined,
2155
2245
  timestamped_set: _not_writable_timestamped_set_func
2156
2246
  });
2157
2247
  } else {
2158
2248
  _Variable_bind_with_timestamped_set.call(this, {
2249
+ set: undefined,
2159
2250
  timestamped_set: _default_writable_timestamped_set_func
2160
2251
  });
2161
2252
  }
@@ -2164,7 +2255,7 @@ function bind_setter(this: UAVariableImpl, options: SetterOptions) {
2164
2255
  interface GetterOptions {
2165
2256
  get?: GetFunc;
2166
2257
  timestamped_get?: TimestampGetFunc;
2167
- refreshFunc?: any;
2258
+ refreshFunc?: (callback: CallbackT<DataValue>) => void;
2168
2259
  dataType?: DataType | string;
2169
2260
  value?: any;
2170
2261
  }
@@ -2174,7 +2265,10 @@ function bind_getter(this: UAVariableImpl, options: GetterOptions) {
2174
2265
  _Variable_bind_with_simple_get.call(this, options);
2175
2266
  } else if (typeof options.timestamped_get === "function") {
2176
2267
  // variation 2
2177
- _Variable_bind_with_timestamped_get.call(this, options);
2268
+ _Variable_bind_with_timestamped_get.call(this, {
2269
+ get: undefined,
2270
+ timestamped_get: options.timestamped_get
2271
+ });
2178
2272
  } else if (typeof options.refreshFunc === "function") {
2179
2273
  // variation 3
2180
2274
  _Variable_bind_with_async_refresh.call(this, options);