node-opcua-address-space 2.91.1 → 2.93.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 (75) hide show
  1. package/dist/source/address_space_ts.d.ts +3 -3
  2. package/dist/source/address_space_ts.js +3 -3
  3. package/dist/source/address_space_ts.js.map +1 -1
  4. package/dist/source/helpers/call_helpers.d.ts +1 -1
  5. package/dist/source/helpers/multiform_func.d.ts +8 -8
  6. package/dist/source/index.d.ts +1 -0
  7. package/dist/source/index.js +3 -1
  8. package/dist/source/index.js.map +1 -1
  9. package/dist/source/interfaces/alarms_and_conditions/ua_condition_ex.d.ts +1 -1
  10. package/dist/source/interfaces/extension_object_constructor.d.ts +1 -1
  11. package/dist/source/interfaces/state_machine/ua_state_machine_type.d.ts +2 -2
  12. package/dist/source/loader/generateAddressSpaceRaw.d.ts +2 -2
  13. package/dist/source/loader/make_xml_extension_object_parser.d.ts +1 -1
  14. package/dist/source/loader/namespace_post_step.d.ts +1 -1
  15. package/dist/source/namespace_machine_state.d.ts +3 -2
  16. package/dist/source/session_context.d.ts +1 -1
  17. package/dist/src/address_space.d.ts +1 -1
  18. package/dist/src/address_space.js +13 -13
  19. package/dist/src/address_space.js.map +1 -1
  20. package/dist/src/alarms_and_conditions/ua_alarm_condition_impl.js +1 -2
  21. package/dist/src/alarms_and_conditions/ua_alarm_condition_impl.js.map +1 -1
  22. package/dist/src/apply_condition_refresh.d.ts +1 -1
  23. package/dist/src/base_node_impl.js +44 -44
  24. package/dist/src/base_node_impl.js.map +1 -1
  25. package/dist/src/data_access/ua_multistate_value_discrete_impl.d.ts +1 -0
  26. package/dist/src/data_access/ua_multistate_value_discrete_impl.js +20 -16
  27. package/dist/src/data_access/ua_multistate_value_discrete_impl.js.map +1 -1
  28. package/dist/src/event_data.d.ts +2 -2
  29. package/dist/src/extension_object_array_node.js +0 -5
  30. package/dist/src/extension_object_array_node.js.map +1 -1
  31. package/dist/src/namespace_impl.d.ts +2 -12
  32. package/dist/src/namespace_impl.js +19 -15
  33. package/dist/src/namespace_impl.js.map +1 -1
  34. package/dist/src/nodeid_manager.d.ts +3 -3
  35. package/dist/src/nodeset_tools/nodeset_to_xml.d.ts +1 -1
  36. package/dist/src/reference_impl.js +6 -6
  37. package/dist/src/reference_impl.js.map +1 -1
  38. package/dist/src/state_machine/finite_state_machine.js +18 -22
  39. package/dist/src/state_machine/finite_state_machine.js.map +1 -1
  40. package/dist/src/tool_isSubtypeOf.d.ts +4 -4
  41. package/dist/src/tool_isSubtypeOf.js +2 -2
  42. package/dist/src/tool_isSubtypeOf.js.map +1 -1
  43. package/dist/src/ua_data_type_impl.js +14 -14
  44. package/dist/src/ua_data_type_impl.js.map +1 -1
  45. package/dist/src/ua_method_impl.js +8 -7
  46. package/dist/src/ua_method_impl.js.map +1 -1
  47. package/dist/src/ua_object_impl.js +7 -7
  48. package/dist/src/ua_object_impl.js.map +1 -1
  49. package/dist/src/ua_object_type_impl.js +8 -8
  50. package/dist/src/ua_object_type_impl.js.map +1 -1
  51. package/dist/src/ua_reference_type_impl.d.ts +1 -1
  52. package/dist/src/ua_reference_type_impl.js +6 -6
  53. package/dist/src/ua_reference_type_impl.js.map +1 -1
  54. package/dist/src/ua_variable_impl.d.ts +4 -0
  55. package/dist/src/ua_variable_impl.js +19 -14
  56. package/dist/src/ua_variable_impl.js.map +1 -1
  57. package/dist/src/ua_variable_impl_ext_obj.js +21 -12
  58. package/dist/src/ua_variable_impl_ext_obj.js.map +1 -1
  59. package/dist/src/ua_variable_type_impl.js +6 -6
  60. package/dist/src/ua_variable_type_impl.js.map +1 -1
  61. package/dist/src/ua_view_impl.js +3 -3
  62. package/dist/src/ua_view_impl.js.map +1 -1
  63. package/package.json +23 -23
  64. package/source/index.ts +2 -3
  65. package/source/namespace_machine_state.ts +5 -2
  66. package/src/address_space.ts +15 -15
  67. package/src/alarms_and_conditions/ua_alarm_condition_impl.ts +1 -2
  68. package/src/data_access/ua_multistate_value_discrete_impl.ts +21 -17
  69. package/src/extension_object_array_node.ts +0 -5
  70. package/src/namespace_impl.ts +30 -20
  71. package/src/state_machine/finite_state_machine.ts +24 -31
  72. package/src/tool_isSubtypeOf.ts +2 -2
  73. package/src/ua_method_impl.ts +6 -6
  74. package/src/ua_variable_impl.ts +9 -3
  75. package/src/ua_variable_impl_ext_obj.ts +26 -16
@@ -11,7 +11,7 @@ import { AccessRestrictionsFlag, coerceLocalizedText, QualifiedNameLike } from "
11
11
  import { QualifiedName } from "node-opcua-data-model";
12
12
  import { BrowseDirection } from "node-opcua-data-model";
13
13
  import { NodeClass } from "node-opcua-data-model";
14
- import { dumpIf, make_errorLog } from "node-opcua-debug";
14
+ import { dumpIf, make_errorLog, make_warningLog } from "node-opcua-debug";
15
15
  import { NodeIdLike, NodeIdType, resolveNodeId } from "node-opcua-nodeid";
16
16
  import { NodeId } from "node-opcua-nodeid";
17
17
  import { StatusCodes } from "node-opcua-status-code";
@@ -143,6 +143,7 @@ function _makeHashKey(nodeId: NodeId): string | number {
143
143
  }
144
144
  const doDebug = false;
145
145
  const errorLog = make_errorLog("AddressSpace");
146
+ const warningLog = make_warningLog("AddressSpace");
146
147
 
147
148
  const regExp1 = /^(s|i|b|g)=/;
148
149
  const regExpNamespaceDotBrowseName = /^[0-9]+:(.*)/;
@@ -469,16 +470,6 @@ export class NamespaceImpl implements NamespacePrivate {
469
470
 
470
471
  /**
471
472
  * add a variable as a component of the parent node
472
- *
473
- * @method addVariable
474
- * @param options
475
- * @param options.browseName the variable name
476
- * @param options.dataType the variable datatype ( "Double", "UInt8" etc...)
477
- * @param [options.typeDefinition="BaseDataVariableType"]
478
- * @param [options.modellingRule=null] the Modelling rule : "Optional" , "Mandatory"
479
- * @param [options.valueRank= -1] the valueRank
480
- * @param [options.arrayDimensions]
481
- * @return UAVariable
482
473
  */
483
474
  public addVariable(options: AddVariableOptions): UAVariable {
484
475
  assert(arguments.length === 1, "Invalid arguments IAddressSpace#addVariable now takes only one argument.");
@@ -581,7 +572,7 @@ export class NamespaceImpl implements NamespacePrivate {
581
572
  options1.nodeClass = NodeClass.ReferenceType;
582
573
  options1.references = options.references || [];
583
574
  options1.nodeId = options.nodeId;
584
-
575
+
585
576
  if (options.subtypeOf) {
586
577
  const subtypeOfNodeId = addressSpace._coerceType(options.subtypeOf, "References", NodeClass.ReferenceType);
587
578
 
@@ -1443,7 +1434,7 @@ export class NamespaceImpl implements NamespacePrivate {
1443
1434
  */
1444
1435
  public addState(
1445
1436
  component: UAStateMachineEx,
1446
- stateName: string,
1437
+ stateName: QualifiedNameLike,
1447
1438
  stateNumber: number,
1448
1439
  isInitialState: boolean
1449
1440
  ): UAState | UAInitialState {
@@ -1454,9 +1445,7 @@ export class NamespaceImpl implements NamespacePrivate {
1454
1445
  const _component = component as UAStateMachineImpl;
1455
1446
 
1456
1447
  assert(_component.nodeClass === NodeClass.Object || _component.nodeClass === NodeClass.ObjectType);
1457
- assert(typeof stateName === "string");
1458
- assert(typeof isInitialState === "boolean");
1459
-
1448
+
1460
1449
  const initialStateType = addressSpace.findObjectType("InitialStateType")!;
1461
1450
  const stateType = addressSpace.findObjectType("StateType")!;
1462
1451
 
@@ -1486,7 +1475,8 @@ export class NamespaceImpl implements NamespacePrivate {
1486
1475
  component: UAStateMachineEx,
1487
1476
  fromState: string,
1488
1477
  toState: string,
1489
- transitionNumber: number
1478
+ transitionNumber: number,
1479
+ browseName?: QualifiedNameLike
1490
1480
  ): UATransitionEx {
1491
1481
  const addressSpace = this.addressSpace;
1492
1482
 
@@ -1497,7 +1487,7 @@ export class NamespaceImpl implements NamespacePrivate {
1497
1487
  assert(typeof toState === "string");
1498
1488
  assert(isFinite(transitionNumber));
1499
1489
 
1500
- const fromStateNode = _component.getComponentByName(fromState, _component.nodeId.namespace);
1490
+ const fromStateNode = _component.getComponentByName(fromState);
1501
1491
 
1502
1492
  // istanbul ignore next
1503
1493
  if (!fromStateNode) {
@@ -1517,8 +1507,11 @@ export class NamespaceImpl implements NamespacePrivate {
1517
1507
  if (!transitionType) {
1518
1508
  throw new Error("Cannot find TransitionType");
1519
1509
  }
1510
+
1511
+ browseName = browseName || fromState + "To" + toState; // "Transition";
1512
+
1520
1513
  const transition = transitionType.instantiate({
1521
- browseName: fromState + "To" + toState + "Transition",
1514
+ browseName,
1522
1515
  componentOf: _component
1523
1516
  }) as UATransitionImpl;
1524
1517
 
@@ -1686,7 +1679,7 @@ export class NamespaceImpl implements NamespacePrivate {
1686
1679
  "\n" +
1687
1680
  "existing node = " +
1688
1681
  exstingNode.toString() +
1689
- "this parent : " + node.parentNodeId?.toString()
1682
+ "this parent : " + node.parentNodeId?.toString()
1690
1683
  );
1691
1684
  }
1692
1685
 
@@ -1994,9 +1987,26 @@ export class NamespaceImpl implements NamespacePrivate {
1994
1987
 
1995
1988
  options.arrayDimensions = options.arrayDimensions || null;
1996
1989
  assert(Array.isArray(options.arrayDimensions) || options.arrayDimensions === null);
1990
+
1997
1991
  // -----------------------------------------------------
1992
+ const hasGetter = (options: AddVariableOptions2) => {
1993
+ return typeof options.value?.get === "function" || typeof options.value?.timestamped_get === "function"
1994
+ }
1995
+
1996
+ // istanbul ignore next
1997
+ if (options.minimumSamplingInterval === undefined && hasGetter(options)) {
1998
+ // a getter has been specified and no options.minimumSamplingInterval has been specified
1999
+ warningLog("[NODE-OPCUA-W30", "namespace#addVariable a getter has been specified and minimumSamplingInterval is missing.\nMinimumSamplingInterval has been adjusted to 1000 ms");
2000
+ options.minimumSamplingInterval = 1000;
2001
+ }
1998
2002
 
1999
2003
  options.minimumSamplingInterval = options.minimumSamplingInterval !== undefined ? +options.minimumSamplingInterval : 0;
2004
+
2005
+ // istanbul ignore next
2006
+ if (options.minimumSamplingInterval === 0 && hasGetter(options)) {
2007
+ warningLog("[NODE-OPCUA-W31", "namespace#addVariable a getter has been specified and minimumSamplingInterval is 0.\nThis may conduct to an unpredicable behavior.\nPlease specify a non zero minimum sampling interval")
2008
+ }
2009
+
2000
2010
  let references = options.references || ([] as AddReferenceOpts[]);
2001
2011
 
2002
2012
  references = ([] as AddReferenceOpts[]).concat(references, [
@@ -25,14 +25,27 @@ const doDebug = false;
25
25
  export declare interface UATransitionImpl extends UATransition, UATransitionEx { }
26
26
  export class UATransitionImpl implements UATransition, UATransitionEx { }
27
27
 
28
- function getComponentFromTypeAndSubtype(typeDef: UAObjectType): UAObject[] {
29
- const components_parts: BaseNode[][] = [];
30
- components_parts.push(typeDef.getComponents());
31
- while (typeDef.subtypeOfObj) {
28
+ function getComponentOfType(typeDef: UAObjectType, typedefinition: UAObjectType): UAObject[] {
29
+
30
+ const get = (typeDef: UAObjectType) =>
31
+ typeDef.getComponents().filter((cc) => {
32
+ const c = cc as UAObject | UAVariable | UAMethod;
33
+ if (c.nodeClass !== NodeClass.Object) return false;
34
+ if (!c.typeDefinitionObj || c.typeDefinitionObj.nodeClass !== NodeClass.ObjectType) {
35
+ return false;
36
+ }
37
+ return c.typeDefinitionObj.isSubtypeOf(typedefinition);
38
+ });
39
+
40
+ let components_parts: BaseNode[] = get(typeDef);
41
+
42
+ while (components_parts.length === 0 && typeDef.subtypeOfObj) {
43
+ // there is no element of that type available in the top level
32
44
  typeDef = typeDef.subtypeOfObj;
33
- components_parts.push(typeDef.getComponents());
45
+ components_parts = get(typeDef);
34
46
  }
35
- return Array.prototype.concat.apply([], components_parts).filter((x: BaseNode) => x.nodeClass === NodeClass.Object);
47
+ return components_parts as UAObject[];
48
+
36
49
  }
37
50
 
38
51
  export interface UAStateMachineImpl {
@@ -86,15 +99,7 @@ export function getFiniteStateMachineTypeStates(uaFiniteStateMachineType: UAObje
86
99
 
87
100
  assert(initialStateType.isSubtypeOf(stateType));
88
101
 
89
-
90
- let comp = getComponentFromTypeAndSubtype(uaFiniteStateMachineType);
91
-
92
- comp = comp.filter((c) => {
93
- if (!c.typeDefinitionObj || c.typeDefinitionObj.nodeClass !== NodeClass.ObjectType) {
94
- return false;
95
- }
96
- return c.typeDefinitionObj.isSubtypeOf(stateType);
97
- });
102
+ const comp = getComponentOfType(uaFiniteStateMachineType, stateType);
98
103
 
99
104
  return comp as UAState[];
100
105
  }
@@ -106,16 +111,7 @@ export function getFiniteStateMachineTypeTransitions(uaFiniteStateMachineType: U
106
111
  if (!transitionType) {
107
112
  throw new Error("cannot find TransitionType");
108
113
  }
109
-
110
- let comp = getComponentFromTypeAndSubtype(uaFiniteStateMachineType);
111
-
112
- comp = comp.filter((c) => {
113
- if (!c.typeDefinitionObj || c.typeDefinitionObj.nodeClass !== NodeClass.ObjectType) {
114
- return false;
115
- }
116
- return c.typeDefinitionObj.isSubtypeOf(transitionType);
117
- });
118
-
114
+ const comp = getComponentOfType(uaFiniteStateMachineType, transitionType);
119
115
  return comp as UATransitionEx[];
120
116
 
121
117
  }
@@ -172,13 +168,10 @@ export class UAStateMachineImpl extends UAObjectImpl implements UAStateMachineEx
172
168
  get initialState(): UAState | null {
173
169
  const addressSpace = this.addressSpace;
174
170
 
175
- const initialStateType = addressSpace.findObjectType("InitialStateType");
171
+ const initialStateType = addressSpace.findObjectType("InitialStateType")!;
176
172
  const typeDef = this.typeDefinitionObj;
177
173
 
178
- let comp = getComponentFromTypeAndSubtype(typeDef);
179
-
180
- comp = comp.filter((c: any) => c.typeDefinitionObj === initialStateType);
181
-
174
+ const comp = getComponentOfType(typeDef, initialStateType);
182
175
  // istanbul ignore next
183
176
  if (comp.length > 1) {
184
177
  throw new Error(" More than 1 initial state in stateMachine");
@@ -444,7 +437,7 @@ registerNodePromoter(ObjectTypeIds.FiniteStateMachineType, promoteToStateMachine
444
437
 
445
438
  export class UAStateMachineTypeImpl extends UAObjectTypeImpl implements UAStateMachineTypeHelper {
446
439
  getStateByName(name: string): UAState | null {
447
- return getFiniteStateMachineTypeStateByName(this, name);
440
+ return getFiniteStateMachineTypeStateByName(this, name);
448
441
  }
449
442
  getStates(): UAState[] {
450
443
  return getFiniteStateMachineTypeStates(this);
@@ -107,13 +107,13 @@ export function construct_isSubtypeOf<T extends UAType>(Class: typeof BaseNodeIm
107
107
  "expecting baseType to be " +
108
108
  Class.name +
109
109
  " but got " +
110
- baseType.constructor.name +
110
+ baseType.toString() +
111
111
  " " +
112
112
  NodeClass[(baseType as BaseNode).nodeClass]
113
113
  );
114
114
  }
115
115
  if (!(this instanceof Class)) {
116
- throw new Error("expecting this to be " + Class.name + " but got " + baseType.constructor.name);
116
+ throw new Error("expecting this to be " + Class.name + " but got " + baseType.toString());
117
117
  }
118
118
  return _slow_isSubtypeOf.call(this, Class, baseType as T);
119
119
  }, hashBaseNode);
@@ -42,7 +42,7 @@ const debugLog = make_debugLog(__filename);
42
42
  const errorLog = make_errorLog(__filename);
43
43
 
44
44
  function default_check_valid_argument(arg: unknown): boolean {
45
- return (arg as any).constructor.name === "Argument";
45
+ return (arg as any) instanceof Argument;
46
46
  }
47
47
 
48
48
  export class UAMethodImpl extends BaseNodeImpl implements UAMethod {
@@ -177,11 +177,11 @@ export class UAMethodImpl extends BaseNodeImpl implements UAMethod {
177
177
  if (object.nodeClass !== NodeClass.Object && object.nodeClass !== NodeClass.ObjectType) {
178
178
  warningLog(
179
179
  "Method " +
180
- this.nodeId.toString() +
181
- " " +
182
- this.browseName.toString() +
183
- " called for a node that is not a Object/ObjectType but " +
184
- NodeClass[context.object!.nodeClass]
180
+ this.nodeId.toString() +
181
+ " " +
182
+ this.browseName.toString() +
183
+ " called for a node that is not a Object/ObjectType but " +
184
+ NodeClass[context.object!.nodeClass]
185
185
  );
186
186
  return callback(null, { statusCode: StatusCodes.BadNodeIdInvalid });
187
187
  }
@@ -138,7 +138,9 @@
138
138
  return (
139
139
  v &&
140
140
  v.constructor &&
141
- (v.constructor.name === "ConstantStatusCode" ||
141
+ (
142
+ v instanceof StatusCode ||
143
+ v.constructor.name === "ConstantStatusCode" ||
142
144
  v.constructor.name === "StatusCode" ||
143
145
  v.constructor.name === "ModifiableStatusCode")
144
146
  );
@@ -537,6 +539,10 @@
537
539
  return this.addressSpacePrivate.isEnumeration(this.dataType);
538
540
  }
539
541
 
542
+ /**
543
+ * return true if the DataType is of type Extension object
544
+ * this is not taking into account the valueRank of the variable
545
+ */
540
546
  public isExtensionObject(): boolean {
541
547
  // DataType must be one of Structure
542
548
  if (this.dataType.isEmpty()) return false;
@@ -1508,7 +1514,7 @@
1508
1514
 
1509
1515
  // coerce to ExtensionObject[] when this.valueRank === 1
1510
1516
  if (optionalExtensionObject && this.valueRank === 1 && !Array.isArray(optionalExtensionObject) && optionalExtensionObject instanceof ExtensionObject) {
1511
- warningLog("bindExtensionObject: coerce to ExtensionObject[] when this.valueRank === 1");
1517
+ warningLog("bindExtensionObject: coerce to ExtensionObject[] when valueRank === 1 and value is a scalar extension object");
1512
1518
  optionalExtensionObject = [optionalExtensionObject];
1513
1519
  }
1514
1520
 
@@ -1800,7 +1806,7 @@
1800
1806
  propagateTouchValueDownward(this, preciseClock, cache);
1801
1807
  }
1802
1808
  } else {
1803
- this.emit("value_changed", this.$dataValue, indexRange);
1809
+ this.emit("value_changed", this.$dataValue.clone(), indexRange);
1804
1810
  }
1805
1811
  }
1806
1812
  }
@@ -104,12 +104,9 @@ 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
107
  if (property.listenerCount("value_changed") > 0) {
109
- const clonedDataValue = property.readValue();
110
- property.emit("value_changed", clonedDataValue);
108
+ property.emit("value_changed", property.$dataValue.clone());
111
109
  }
112
- // }
113
110
  }
114
111
 
115
112
  export function propagateTouchValueUpward(self: UAVariableImpl, now: PreciseClock, cache?: Set<UAVariable>): void {
@@ -346,6 +343,7 @@ function _installFields2(uaVariable: UAVariableImpl, { get, set }: {
346
343
  const sourceTime = coerceClock(dataValue.sourceTimestamp, dataValue.sourcePicoseconds);
347
344
  const value = dataValue.value.value;
348
345
  set(field.name!, value, sourceTime);
346
+ propertyNode.touchValue(sourceTime);
349
347
  }
350
348
 
351
349
  if (propertyNode.dataTypeObj.basicDataType === DataType.ExtensionObject) {
@@ -396,9 +394,10 @@ function isVariableContainingExtensionObject(uaVariable: UAVariableImpl): boolea
396
394
  }
397
395
 
398
396
  function _innerBindExtensionObjectScalar(uaVariable: UAVariableImpl,
399
- { get, set }: {
397
+ { get, set, setField }: {
400
398
  get: () => ExtensionObject;
401
399
  set: (value: ExtensionObject, sourceTimestamp: PreciseClock, cache: Set<UAVariableImpl>) => void;
400
+ setField: (fieldName: string, value: any, sourceTimestamp: PreciseClock, cache?: Set<UAVariableImpl>) => void;
402
401
  },
403
402
  options?: BindExtensionObjectOptions
404
403
  ) {
@@ -420,10 +419,7 @@ function _innerBindExtensionObjectScalar(uaVariable: UAVariableImpl,
420
419
  return extObj[lowerFirstLetter(fieldName)];
421
420
  },
422
421
  set: (fieldName: string, value: any, sourceTime: PreciseClock) => {
423
- const extObj = get() as any;
424
- extObj[lowerFirstLetter(fieldName)] = value;
425
- //1 propagateTouchValueDownward(uaVariable, sourceTime);
426
- //1 propagateTouchValueUpward(uaVariable, sourceTime);
422
+ setField(fieldName, value, sourceTime);
427
423
  }
428
424
  }, options);
429
425
 
@@ -497,7 +493,7 @@ export function _bindExtensionObject(
497
493
  "bindExtensionObject: $extensionObject is incorrect: we are expecting a " +
498
494
  uaVariable.dataType.toString({ addressSpace: uaVariable.addressSpace }) +
499
495
  " but we got a " +
500
- uaVariable.$extensionObject?.constructor.name
496
+ uaVariable.$extensionObject?.schema.name
501
497
  );
502
498
  }
503
499
  return uaVariable.$extensionObject;
@@ -530,7 +526,12 @@ export function _bindExtensionObject(
530
526
  _innerBindExtensionObjectScalar(uaVariable,
531
527
  {
532
528
  get: () => uaVariable.$extensionObject,
533
- set: (value: ExtensionObject) => installExt(uaVariable, value)
529
+ set: (value: ExtensionObject) => installExt(uaVariable, value),
530
+ setField: (fieldName: string, value: any) => {
531
+ const extObj = uaVariable.$extensionObject;
532
+ getProxyTarget(extObj)[lowerFirstLetter(fieldName)] = value;
533
+ }
534
+
534
535
  }, options);
535
536
  return;
536
537
  } else if (uaVariable.valueRank === 1 /** Array */) {
@@ -546,7 +547,11 @@ export function _bindExtensionObject(
546
547
  _innerBindExtensionObjectScalar(uaVariable,
547
548
  {
548
549
  get: () => uaVariable.$extensionObject,
549
- set: (value: ExtensionObject) => installExt(uaVariable, value)
550
+ set: (value: ExtensionObject) => installExt(uaVariable, value),
551
+ setField: (fieldName: string, value: any) => {
552
+ const extObj = uaVariable.$extensionObject;
553
+ getProxyTarget(extObj)[lowerFirstLetter(fieldName)] = value;
554
+ }
550
555
  }, options);
551
556
  }
552
557
  }
@@ -575,7 +580,7 @@ export function _bindExtensionObjectArrayOrMatrix(
575
580
  options?: BindExtensionObjectOptions
576
581
  ): ExtensionObject[] {
577
582
 
578
- options = options || { createMissingProp: false};
583
+ options = options || { createMissingProp: false };
579
584
  options.createMissingProp = options.createMissingProp || false;
580
585
 
581
586
  // istanbul ignore next
@@ -592,7 +597,7 @@ export function _bindExtensionObjectArrayOrMatrix(
592
597
  assert(Array.isArray(uaVariable.$dataValue.value.value));
593
598
  optionalExtensionObjectArray = uaVariable.$dataValue.value.value;
594
599
  }
595
-
600
+
596
601
  if ((arrayDimensions.length === 0 || arrayDimensions.length === 1 && arrayDimensions[0] === 0) && optionalExtensionObjectArray) {
597
602
  arrayDimensions[0] = optionalExtensionObjectArray.length;
598
603
  }
@@ -652,7 +657,7 @@ export function _bindExtensionObjectArrayOrMatrix(
652
657
  if (!options.createMissingProp) {
653
658
  continue;
654
659
  }
655
-
660
+
656
661
  uaElement = namespace.addVariable({
657
662
  browseName,
658
663
  nodeId,
@@ -678,7 +683,6 @@ export function _bindExtensionObjectArrayOrMatrix(
678
683
  {
679
684
  get: () => uaVariable.$$extensionObjectArray[capturedIndex],
680
685
  set: (newValue: ExtensionObject, sourceTimestamp: PreciseClock, cache: Set<UAVariableImpl>) => {
681
-
682
686
  assert(!isProxy(uaVariable.$$extensionObjectArray[capturedIndex]));
683
687
  uaVariable.$$extensionObjectArray[capturedIndex] = newValue;
684
688
  if (uaVariable.$$extensionObjectArray !== uaVariable.$dataValue.value.value) {
@@ -687,6 +691,12 @@ export function _bindExtensionObjectArrayOrMatrix(
687
691
  }
688
692
  propagateTouchValueDownward(capturedUaElement, sourceTimestamp, cache);
689
693
  propagateTouchValueUpward(capturedUaElement, sourceTimestamp, cache);
694
+ },
695
+ setField: (fieldName: string, newValue: any, sourceTimestamp: PreciseClock, cache?: Set<UAVariableImpl>) => {
696
+ // istanbul ignore next doDebug && debugLog("setField", fieldName, newValue, sourceTimestamp, cache);
697
+ const extObj = uaVariable.$$extensionObjectArray[capturedIndex];
698
+ (isProxy(extObj) ? getProxyTarget(extObj) : extObj)[lowerFirstLetter(fieldName)] = newValue;
699
+ propagateTouchValueUpward(capturedUaElement, sourceTimestamp, cache);
690
700
  }
691
701
  }, { ...options, force: true });
692
702