node-opcua-address-space 2.182.2 → 2.183.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.
- package/api/helpers/argument_list.ts +8 -9
- package/api/helpers/call_helpers.ts +2 -0
- package/api/index.ts +14 -0
- package/api/interfaces/alarms_and_conditions/deviation_stuff.ts +30 -0
- package/api/interfaces/alarms_and_conditions/promote_to_alarm_options.ts +24 -0
- package/api/interfaces/alarms_and_conditions/ua_alarm_condition_ex.ts +33 -0
- package/api/interfaces/alarms_and_conditions/ua_limit_alarm_ex.ts +18 -0
- package/api/loader/nodeset_record.ts +2 -2
- package/api/loader/nodeset_record_applier.ts +1 -0
- package/api/loader/nodeset_xml_producer.ts +5 -1
- package/api/loader/register_node_promoter.ts +10 -2
- package/api/namespace_of.ts +28 -0
- package/dist/api/helpers/argument_list.js +8 -7
- package/dist/api/helpers/argument_list.js.map +1 -1
- package/dist/api/helpers/call_helpers.js +2 -0
- package/dist/api/helpers/call_helpers.js.map +1 -1
- package/dist/api/index.d.ts +11 -0
- package/dist/api/index.js +16 -3
- package/dist/api/index.js.map +1 -1
- package/dist/api/interfaces/alarms_and_conditions/deviation_stuff.d.ts +28 -0
- package/dist/api/interfaces/alarms_and_conditions/promote_to_alarm_options.d.ts +23 -0
- package/dist/api/interfaces/alarms_and_conditions/promote_to_alarm_options.js +3 -0
- package/dist/api/interfaces/alarms_and_conditions/promote_to_alarm_options.js.map +1 -0
- package/dist/api/interfaces/alarms_and_conditions/ua_alarm_condition_ex.d.ts +31 -0
- package/dist/api/interfaces/alarms_and_conditions/ua_limit_alarm_ex.d.ts +17 -0
- package/dist/api/loader/nodeset_record.d.ts +1 -1
- package/dist/api/loader/nodeset_record.js +1 -1
- package/dist/api/loader/nodeset_record_applier.js +1 -0
- package/dist/api/loader/nodeset_record_applier.js.map +1 -1
- package/dist/api/loader/nodeset_xml_producer.js +7 -1
- package/dist/api/loader/nodeset_xml_producer.js.map +1 -1
- package/dist/api/loader/register_node_promoter.d.ts +9 -1
- package/dist/api/loader/register_node_promoter.js +9 -2
- package/dist/api/loader/register_node_promoter.js.map +1 -1
- package/dist/api/namespace_of.d.ts +23 -0
- package/dist/api/namespace_of.js +29 -0
- package/dist/api/namespace_of.js.map +1 -0
- package/dist/impl/address_space_change_event_tools.js +4 -1
- package/dist/impl/address_space_change_event_tools.js.map +1 -1
- package/dist/impl/address_space_private.d.ts +6 -6
- package/dist/impl/alarms_and_conditions/deviation_alarm_helper.js +3 -0
- package/dist/impl/alarms_and_conditions/deviation_alarm_helper.js.map +1 -1
- package/dist/impl/alarms_and_conditions/ua_acknowledgeable_condition_impl.d.ts +7 -0
- package/dist/impl/alarms_and_conditions/ua_acknowledgeable_condition_impl.js +47 -37
- package/dist/impl/alarms_and_conditions/ua_acknowledgeable_condition_impl.js.map +1 -1
- package/dist/impl/alarms_and_conditions/ua_alarm_condition_impl.d.ts +72 -2
- package/dist/impl/alarms_and_conditions/ua_alarm_condition_impl.js +220 -101
- package/dist/impl/alarms_and_conditions/ua_alarm_condition_impl.js.map +1 -1
- package/dist/impl/alarms_and_conditions/ua_condition_impl.d.ts +19 -0
- package/dist/impl/alarms_and_conditions/ua_condition_impl.js +71 -16
- package/dist/impl/alarms_and_conditions/ua_condition_impl.js.map +1 -1
- package/dist/impl/alarms_and_conditions/ua_exclusive_deviation_alarm_impl.d.ts +9 -1
- package/dist/impl/alarms_and_conditions/ua_exclusive_deviation_alarm_impl.js +13 -3
- package/dist/impl/alarms_and_conditions/ua_exclusive_deviation_alarm_impl.js.map +1 -1
- package/dist/impl/alarms_and_conditions/ua_exclusive_limit_alarm_impl.d.ts +2 -2
- package/dist/impl/alarms_and_conditions/ua_exclusive_limit_alarm_impl.js +4 -4
- package/dist/impl/alarms_and_conditions/ua_exclusive_limit_alarm_impl.js.map +1 -1
- package/dist/impl/alarms_and_conditions/ua_limit_alarm_impl.d.ts +13 -2
- package/dist/impl/alarms_and_conditions/ua_limit_alarm_impl.js +21 -5
- package/dist/impl/alarms_and_conditions/ua_limit_alarm_impl.js.map +1 -1
- package/dist/impl/alarms_and_conditions/ua_non_exclusive_deviation_alarm_impl.d.ts +9 -1
- package/dist/impl/alarms_and_conditions/ua_non_exclusive_deviation_alarm_impl.js +13 -3
- package/dist/impl/alarms_and_conditions/ua_non_exclusive_deviation_alarm_impl.js.map +1 -1
- package/dist/impl/alarms_and_conditions/ua_non_exclusive_limit_alarm_impl.d.ts +1 -1
- package/dist/impl/alarms_and_conditions/ua_non_exclusive_limit_alarm_impl.js +2 -2
- package/dist/impl/alarms_and_conditions/ua_non_exclusive_limit_alarm_impl.js.map +1 -1
- package/dist/impl/alarms_and_conditions/ua_off_normal_alarm_impl.d.ts +1 -1
- package/dist/impl/alarms_and_conditions/ua_off_normal_alarm_impl.js +2 -2
- package/dist/impl/alarms_and_conditions/ua_off_normal_alarm_impl.js.map +1 -1
- package/dist/impl/base_node_impl.d.ts +2 -0
- package/dist/impl/base_node_impl.js +28 -0
- package/dist/impl/base_node_impl.js.map +1 -1
- package/dist/impl/base_node_private.d.ts +2 -0
- package/dist/impl/base_node_private.js.map +1 -1
- package/dist/impl/data_access/adjust_datavalue_status_code.d.ts +2 -2
- package/dist/impl/data_access/adjust_datavalue_status_code.js +35 -4
- package/dist/impl/data_access/adjust_datavalue_status_code.js.map +1 -1
- package/dist/impl/data_access/semantics_changed.d.ts +31 -0
- package/dist/impl/data_access/semantics_changed.js +77 -0
- package/dist/impl/data_access/semantics_changed.js.map +1 -0
- package/dist/impl/data_access/ua_multistate_discrete_impl.js +0 -2
- package/dist/impl/data_access/ua_multistate_discrete_impl.js.map +1 -1
- package/dist/impl/data_access/ua_two_state_discrete_impl.js +6 -15
- package/dist/impl/data_access/ua_two_state_discrete_impl.js.map +1 -1
- package/dist/impl/historical_access/read_processed_details_hook.d.ts +22 -0
- package/dist/impl/historical_access/read_processed_details_hook.js +12 -0
- package/dist/impl/historical_access/read_processed_details_hook.js.map +1 -0
- package/dist/impl/namespace_impl.js +2 -4
- package/dist/impl/namespace_impl.js.map +1 -1
- package/dist/impl/nodeid_manager.d.ts +5 -0
- package/dist/impl/nodeid_manager.js +21 -5
- package/dist/impl/nodeid_manager.js.map +1 -1
- package/dist/impl/ua_object_impl.js +13 -1
- package/dist/impl/ua_object_impl.js.map +1 -1
- package/dist/impl/ua_object_type_impl.js +1 -0
- package/dist/impl/ua_object_type_impl.js.map +1 -1
- package/dist/impl/ua_variable_impl.js +12 -1
- package/dist/impl/ua_variable_impl.js.map +1 -1
- package/dist/impl/ua_variable_type_impl.js +1 -0
- package/dist/impl/ua_variable_type_impl.js.map +1 -1
- package/dist/tsconfig_base.tsbuildinfo +1 -1
- package/distHelpers/get_address_space_fixture.js +2 -1
- package/distHelpers/get_address_space_fixture.js.map +1 -1
- package/distHelpers/index.d.ts +1 -0
- package/distHelpers/index.js +1 -0
- package/distHelpers/index.js.map +1 -1
- package/distNodeJS/generate_address_space.js +5 -1
- package/distNodeJS/generate_address_space.js.map +1 -1
- package/impl/address_space_change_event_tools.ts +4 -1
- package/impl/address_space_private.ts +5 -17
- package/impl/alarms_and_conditions/deviation_alarm_helper.ts +3 -0
- package/impl/alarms_and_conditions/ua_acknowledgeable_condition_impl.ts +53 -41
- package/impl/alarms_and_conditions/ua_alarm_condition_impl.ts +262 -111
- package/impl/alarms_and_conditions/ua_condition_impl.ts +94 -20
- package/impl/alarms_and_conditions/ua_exclusive_deviation_alarm_impl.ts +14 -3
- package/impl/alarms_and_conditions/ua_exclusive_limit_alarm_impl.ts +4 -4
- package/impl/alarms_and_conditions/ua_limit_alarm_impl.ts +22 -5
- package/impl/alarms_and_conditions/ua_non_exclusive_deviation_alarm_impl.ts +14 -3
- package/impl/alarms_and_conditions/ua_non_exclusive_limit_alarm_impl.ts +2 -2
- package/impl/alarms_and_conditions/ua_off_normal_alarm_impl.ts +2 -2
- package/impl/base_node_impl.ts +31 -0
- package/impl/base_node_private.ts +2 -0
- package/impl/data_access/adjust_datavalue_status_code.ts +36 -7
- package/impl/data_access/semantics_changed.ts +102 -0
- package/impl/data_access/ua_multistate_discrete_impl.ts +0 -2
- package/impl/data_access/ua_two_state_discrete_impl.ts +6 -16
- package/impl/historical_access/read_processed_details_hook.ts +35 -0
- package/impl/namespace_impl.ts +2 -6
- package/impl/nodeid_manager.ts +27 -5
- package/impl/ua_object_impl.ts +13 -1
- package/impl/ua_object_type_impl.ts +1 -0
- package/impl/ua_variable_impl.ts +12 -1
- package/impl/ua_variable_type_impl.ts +1 -0
- package/package.json +39 -39
- package/source_nodejs/generate_address_space.ts +6 -1
|
@@ -203,7 +203,7 @@ export class UALimitAlarmImplBase extends UAAlarmConditionImplBase implements UA
|
|
|
203
203
|
this.lowLowLimit.setValueFromSource({ dataType: DataType.Double, value });
|
|
204
204
|
}
|
|
205
205
|
|
|
206
|
-
|
|
206
|
+
public onInputDataValueChange(dataValue: DataValue): void {
|
|
207
207
|
assert(dataValue instanceof DataValue);
|
|
208
208
|
|
|
209
209
|
if (dataValue.statusCode.equals(StatusCodes.BadWaitingForInitialData)) {
|
|
@@ -212,20 +212,37 @@ export class UALimitAlarmImplBase extends UAAlarmConditionImplBase implements UA
|
|
|
212
212
|
}
|
|
213
213
|
if (dataValue.statusCode.isNotGood() && !dataValue.statusCode.equals(StatusCodes.UncertainInitialValue)) {
|
|
214
214
|
// genuinely bad status (not the initial uncertain state) → no specific limit state, alarm inactive
|
|
215
|
-
this.
|
|
215
|
+
this.signalNewCondition(null, false, "Input node value is not good");
|
|
216
216
|
return;
|
|
217
217
|
}
|
|
218
218
|
if (dataValue.value.dataType === DataType.Null) {
|
|
219
219
|
// input not yet set → no specific limit state, alarm inactive
|
|
220
|
-
this.
|
|
220
|
+
this.signalNewCondition(null, false, "Input node value is null");
|
|
221
221
|
return;
|
|
222
222
|
}
|
|
223
223
|
const value = dataValue.value.value;
|
|
224
|
+
// deliberately the deprecated name, for the same reason as _calculateConditionInfo:
|
|
225
|
+
// an old subclass that overrode `_setStateBasedOnInputValue` is still reached, and a
|
|
226
|
+
// new assignment of the published name is reached through the delegate below.
|
|
224
227
|
this._setStateBasedOnInputValue(value);
|
|
225
228
|
}
|
|
226
229
|
|
|
227
|
-
|
|
228
|
-
|
|
230
|
+
/**
|
|
231
|
+
* How the alarm turns a numeric input value into limit states. Assign to it to give a
|
|
232
|
+
* limit alarm a rule of its own.
|
|
233
|
+
*
|
|
234
|
+
* The base implementation throws: a bare LimitAlarmType has no limit states to set, so
|
|
235
|
+
* every concrete limit alarm type defines this.
|
|
236
|
+
*/
|
|
237
|
+
public setStateBasedOnInputValue(_value: number): void {
|
|
238
|
+
throw new Error("setStateBasedOnInputValue must be overriden");
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* @deprecated assign {@link setStateBasedOnInputValue} instead; this delegates to it.
|
|
243
|
+
*/
|
|
244
|
+
public _setStateBasedOnInputValue(value: number): void {
|
|
245
|
+
this.setStateBasedOnInputValue(value);
|
|
229
246
|
}
|
|
230
247
|
|
|
231
248
|
protected _watchLimits(): void {
|
|
@@ -61,14 +61,14 @@ export class UANonExclusiveDeviationAlarmImplBase
|
|
|
61
61
|
|
|
62
62
|
return alarm;
|
|
63
63
|
}
|
|
64
|
-
public
|
|
64
|
+
public setStateBasedOnInputValue(value: number): void {
|
|
65
65
|
const setpointValue = this.getSetpointValue();
|
|
66
66
|
if (setpointValue === null) {
|
|
67
67
|
throw new Error("Cannot access setpoint Value");
|
|
68
68
|
}
|
|
69
69
|
assert(Number.isFinite(setpointValue), "expecting a valid setpoint value");
|
|
70
70
|
// call base class implementation
|
|
71
|
-
super.
|
|
71
|
+
super.setStateBasedOnInputValue(value - setpointValue);
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
public getSetpointNodeNode(): UAVariableT<number, DataType.Double> | UAVariableT<number, DataType.Float> | undefined {
|
|
@@ -79,10 +79,21 @@ export class UANonExclusiveDeviationAlarmImplBase
|
|
|
79
79
|
return DeviationAlarmHelper_getSetpointValue.call(this);
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
/**
|
|
83
|
+
* What the alarm does when the value of its setpoint node changes: re-evaluate the state
|
|
84
|
+
* of the alarm against the new setpoint, since the deviation it watches has just moved.
|
|
85
|
+
*/
|
|
86
|
+
public onSetpointDataValueChange(dataValue: DataValue): void {
|
|
83
87
|
DeviationAlarmHelper_onSetpointDataValueChange.call(this, dataValue);
|
|
84
88
|
}
|
|
85
89
|
|
|
90
|
+
/**
|
|
91
|
+
* @deprecated assign {@link onSetpointDataValueChange} instead; this delegates to it.
|
|
92
|
+
*/
|
|
93
|
+
public _onSetpointDataValueChange(dataValue: DataValue): void {
|
|
94
|
+
this.onSetpointDataValueChange(dataValue);
|
|
95
|
+
}
|
|
96
|
+
|
|
86
97
|
public _install_setpoint(options: InstallSetPointOptions): void {
|
|
87
98
|
DeviationAlarmHelper_install_setpoint.call(this, options);
|
|
88
99
|
}
|
|
@@ -180,9 +180,9 @@ export class UANonExclusiveLimitAlarmImplBase extends UALimitAlarmImpl implement
|
|
|
180
180
|
|
|
181
181
|
state_str = JSON.stringify(states);
|
|
182
182
|
|
|
183
|
-
UALimitAlarmImpl.prototype.
|
|
183
|
+
UALimitAlarmImpl.prototype.signalNewCondition.call(this, state_str, isActive, value);
|
|
184
184
|
}
|
|
185
|
-
|
|
185
|
+
public setStateBasedOnInputValue(value: number): void {
|
|
186
186
|
assert(Number.isFinite(value), "expecting a valid value here");
|
|
187
187
|
|
|
188
188
|
let isActive = false;
|
|
@@ -168,7 +168,7 @@ export class UAOffNormalAlarmImplBase extends UADiscreteAlarmImplBase implements
|
|
|
168
168
|
}
|
|
169
169
|
const stateName = isActive ? "Active" : "Inactive";
|
|
170
170
|
// also raise the event
|
|
171
|
-
this.
|
|
171
|
+
this.signalNewCondition(stateName, isActive, message);
|
|
172
172
|
if (!isActive) {
|
|
173
173
|
this.currentBranch().setRetain(false);
|
|
174
174
|
}
|
|
@@ -183,7 +183,7 @@ export class UAOffNormalAlarmImplBase extends UADiscreteAlarmImplBase implements
|
|
|
183
183
|
this.updateAlarmState(isActive, "automatique update");
|
|
184
184
|
}
|
|
185
185
|
|
|
186
|
-
|
|
186
|
+
public onInputDataValueChange(dataValue: DataValue): void {
|
|
187
187
|
if (dataValue.statusCode.isNotGood()) {
|
|
188
188
|
// what shall we do ?
|
|
189
189
|
return;
|
package/impl/base_node_impl.ts
CHANGED
|
@@ -165,6 +165,8 @@ export interface InternalBaseNodeOptions {
|
|
|
165
165
|
browseName: QualifiedName;
|
|
166
166
|
nodeId: NodeId;
|
|
167
167
|
references?: UAReference[];
|
|
168
|
+
/** the parent the document declared (ParentNodeId), see CreateNodeOptions.parentNodeId */
|
|
169
|
+
parentNodeId?: NodeIdLike | BaseNode | null;
|
|
168
170
|
|
|
169
171
|
displayName?: LocalizedTextLike | LocalizedTextLike[];
|
|
170
172
|
description?: LocalizedTextLike | null;
|
|
@@ -443,6 +445,14 @@ export abstract class BaseNodeImpl<T extends BaseNodeEvents & ListenerSignature<
|
|
|
443
445
|
if (options.releaseStatus) {
|
|
444
446
|
this.releaseStatus = options.releaseStatus;
|
|
445
447
|
}
|
|
448
|
+
if (options.parentNodeId) {
|
|
449
|
+
_private._declaredParentNodeId =
|
|
450
|
+
options.parentNodeId instanceof NodeId
|
|
451
|
+
? options.parentNodeId
|
|
452
|
+
: (options.parentNodeId as BaseNode).nodeId instanceof NodeId
|
|
453
|
+
? (options.parentNodeId as BaseNode).nodeId
|
|
454
|
+
: resolveNodeId(options.parentNodeId as NodeIdLike);
|
|
455
|
+
}
|
|
446
456
|
if (options.declaredAccessRestrictions !== undefined) {
|
|
447
457
|
this.declaredAccessRestrictions = options.declaredAccessRestrictions;
|
|
448
458
|
}
|
|
@@ -1736,6 +1746,27 @@ function _setup_parent_item(this: BaseNode, referencesMap: Map<ReferenceKey, UAR
|
|
|
1736
1746
|
const addressSpace = this.addressSpace;
|
|
1737
1747
|
|
|
1738
1748
|
if (referencesMap.size > 0) {
|
|
1749
|
+
// the document declared a parent (ParentNodeId): it wins when it is one of the
|
|
1750
|
+
// hierarchical parents, whatever the order the references were declared in, and
|
|
1751
|
+
// even through Organizes, which is otherwise not a parent relation. OPC Foundation
|
|
1752
|
+
// NodeSets rely on it for a node reached from two parents (an add-in re-exposed
|
|
1753
|
+
// from a folder) and for a folder a type organizes, and name ids after it.
|
|
1754
|
+
const declared = _private._declaredParentNodeId;
|
|
1755
|
+
if (declared) {
|
|
1756
|
+
const hierarchical = this.findReferencesEx("HierarchicalReferences", BrowseDirection.Inverse);
|
|
1757
|
+
const match = hierarchical.find((r) => sameNodeId(r.nodeId, declared));
|
|
1758
|
+
if (match) {
|
|
1759
|
+
return ReferenceImpl.resolveReferenceNode(addressSpace, match);
|
|
1760
|
+
}
|
|
1761
|
+
// the parent may be asked for while the document is still being loaded, before
|
|
1762
|
+
// the other end of the reference has been declared: the document still says
|
|
1763
|
+
// who the parent is
|
|
1764
|
+
const declaredNode = addressSpace.findNode(declared);
|
|
1765
|
+
if (declaredNode) {
|
|
1766
|
+
return declaredNode;
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
|
|
1739
1770
|
references = this.findReferencesEx("Aggregates", BrowseDirection.Inverse);
|
|
1740
1771
|
|
|
1741
1772
|
if (references.length >= 1) {
|
|
@@ -112,6 +112,8 @@ interface BaseNodeCache {
|
|
|
112
112
|
_displayName?: LocalizedText[];
|
|
113
113
|
_displayNameRaw?: LocalizedTextLike | LocalizedTextLike[];
|
|
114
114
|
_parent?: BaseNode | null;
|
|
115
|
+
/** the parent the document declared (`ParentNodeId`); consulted once, when _parent is first computed */
|
|
116
|
+
_declaredParentNodeId?: NodeId;
|
|
115
117
|
/**
|
|
116
118
|
* the references other nodes hold to this one; `EMPTY_REFERENCE_INDEX` until the first one
|
|
117
119
|
* arrives, since a nodeset declares most references from both ends and most nodes never get one
|
|
@@ -7,18 +7,47 @@ import { NodeClass } from "node-opcua-data-model";
|
|
|
7
7
|
import type { DataValue } from "node-opcua-data-value";
|
|
8
8
|
import { type StatusCode, StatusCodes } from "node-opcua-status-code";
|
|
9
9
|
import type { Range } from "node-opcua-types";
|
|
10
|
-
import type
|
|
11
|
-
import type { UAVariableImpl } from "../ua_variable_impl.js";
|
|
10
|
+
import { type Variant, VariantArrayType } from "node-opcua-variant";
|
|
12
11
|
|
|
13
|
-
function
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
function isOutOfRange(value: number, range: Range): boolean {
|
|
13
|
+
return value < range.low || value > range.high;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Checks that the value(s) carried by `variant` fall within `range`.
|
|
18
|
+
*
|
|
19
|
+
* `variant.value` is a plain number for a scalar write, but for an array (or matrix)
|
|
20
|
+
* variable it is an array-like (Array or a TypedArray) holding only the elements this
|
|
21
|
+
* particular Write touches - a client writing with an IndexRange sends just the sub-range
|
|
22
|
+
* it targets, so an out-of-range element elsewhere in the variable's full value (one this
|
|
23
|
+
* write leaves untouched) must never be seen here and must never fail the write. Comparing
|
|
24
|
+
* an array directly with `<`/`>` coerces it through Array.prototype.toString first: a
|
|
25
|
+
* one-element array happens to stringify to its bare element and compares correctly by
|
|
26
|
+
* accident, but any other length stringifies to a comma-joined list that parses to NaN,
|
|
27
|
+
* so every such comparison is silently false and a genuinely out-of-range multi-element
|
|
28
|
+
* write was never being rejected. Each element is compared numerically instead.
|
|
29
|
+
*/
|
|
30
|
+
function validate_value_range(range: Range, variant: Variant): boolean {
|
|
31
|
+
const value = variant.value;
|
|
32
|
+
if (variant.arrayType === VariantArrayType.Array || variant.arrayType === VariantArrayType.Matrix) {
|
|
33
|
+
if (value === null || value === undefined) {
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
const length = (value as ArrayLike<number>).length;
|
|
37
|
+
for (let i = 0; i < length; i++) {
|
|
38
|
+
if (isOutOfRange((value as ArrayLike<number>)[i], range)) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return true;
|
|
16
43
|
}
|
|
17
|
-
return
|
|
44
|
+
return !isOutOfRange(value as number, range);
|
|
18
45
|
}
|
|
19
46
|
|
|
20
47
|
export function adjustDataValueStatusCode(
|
|
21
|
-
|
|
48
|
+
// the public interface, not UAVariableImpl: this function is exported from api/, and the
|
|
49
|
+
// body only reads a child node, so naming an internal type here leaked it into the API
|
|
50
|
+
variable: UAVariable,
|
|
22
51
|
dataValue: DataValue,
|
|
23
52
|
acceptValueOutOfRange: boolean
|
|
24
53
|
): StatusCode {
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module node-opcua-address-space
|
|
3
|
+
*
|
|
4
|
+
* OPC 10000-8 (Data Access) 5.2: some Properties of a DataItem carry the *meaning* of its
|
|
5
|
+
* value rather than the value itself. When one of them changes, a client that has cached the
|
|
6
|
+
* old meaning would misinterpret every subsequent value, so the Server has to say so: the
|
|
7
|
+
* next data change notification of the DataItem carries the SemanticsChanged bit of
|
|
8
|
+
* OPC 10000-4 7.39.
|
|
9
|
+
*
|
|
10
|
+
* The address space side of that is `UAVariable#handle_semantic_changed()`, which bumps
|
|
11
|
+
* `semantic_version`; the MonitoredItem watches that counter. What was missing is the wiring
|
|
12
|
+
* between a Property and its DataItem: the `addAnalogDataItem` / `addTwoStateDiscrete` /
|
|
13
|
+
* `addMultiStateDiscrete` helpers each attached a "value_changed" listener by hand, so only a
|
|
14
|
+
* node built through one of those helpers was covered. A DataItem instantiated from its
|
|
15
|
+
* VariableType, loaded from a NodeSet, or assembled property by property (which is the only
|
|
16
|
+
* way to build the ArrayItemType subtypes) had none, and its EURange could be written without
|
|
17
|
+
* any notification ever carrying the bit.
|
|
18
|
+
*
|
|
19
|
+
* So the relation is resolved from the Property instead, at the moment its value changes:
|
|
20
|
+
* a Property named after one of the semantics-bearing Properties below, whose parent is a
|
|
21
|
+
* DataItem, notifies that parent. That covers every way a DataItem can come into existence,
|
|
22
|
+
* and needs no bookkeeping at construction time - which is what defeated a creation-time hook,
|
|
23
|
+
* since a hand-built DataItem gets its Properties *after* the Variable itself.
|
|
24
|
+
*/
|
|
25
|
+
import type { UAVariable, UAVariableType } from "node-opcua-address-space-base";
|
|
26
|
+
import { VariableTypeIds } from "node-opcua-constants";
|
|
27
|
+
import { NodeClass } from "node-opcua-data-model";
|
|
28
|
+
import { resolveNodeId } from "node-opcua-nodeid";
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* the Properties whose value carries the semantics of the DataItem they belong to.
|
|
32
|
+
*
|
|
33
|
+
* DataItemType : Definition, ValuePrecision
|
|
34
|
+
* AnalogItemType/UnitType : EURange, InstrumentRange, EngineeringUnits
|
|
35
|
+
* ArrayItemType (+subtypes): Title, AxisScaleType, XAxisDefinition, YAxisDefinition, ZAxisDefinition
|
|
36
|
+
* TwoStateDiscreteType : TrueState, FalseState
|
|
37
|
+
* MultiStateDiscreteType : EnumStrings
|
|
38
|
+
* MultiStateValueDiscrete : EnumValues
|
|
39
|
+
*/
|
|
40
|
+
const semanticsBearingProperties = new Set<string>([
|
|
41
|
+
"AxisScaleType",
|
|
42
|
+
"Definition",
|
|
43
|
+
"EngineeringUnits",
|
|
44
|
+
"EURange",
|
|
45
|
+
"EnumStrings",
|
|
46
|
+
"EnumValues",
|
|
47
|
+
"FalseState",
|
|
48
|
+
"InstrumentRange",
|
|
49
|
+
"Title",
|
|
50
|
+
"TrueState",
|
|
51
|
+
"ValuePrecision",
|
|
52
|
+
"XAxisDefinition",
|
|
53
|
+
"YAxisDefinition",
|
|
54
|
+
"ZAxisDefinition"
|
|
55
|
+
]);
|
|
56
|
+
|
|
57
|
+
const dataItemTypeNodeId = resolveNodeId(VariableTypeIds.DataItemType);
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* the DataItem whose semantics `property` carries, or null when `property` is not one of them.
|
|
61
|
+
*
|
|
62
|
+
* A positive answer is memoized on the Property; a negative one is not, because the name test
|
|
63
|
+
* that gates the lookup is a single Set hit and because a Property can be attached to its
|
|
64
|
+
* DataItem after its first value change.
|
|
65
|
+
*/
|
|
66
|
+
function semanticsOwnerOf(property: UAVariable): UAVariable | null {
|
|
67
|
+
const name = property.browseName.name;
|
|
68
|
+
if (!name || !semanticsBearingProperties.has(name)) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
const cache = property as UAVariable & { $$semanticsOwner?: UAVariable };
|
|
72
|
+
if (cache.$$semanticsOwner) {
|
|
73
|
+
return cache.$$semanticsOwner;
|
|
74
|
+
}
|
|
75
|
+
const parent = property.parent;
|
|
76
|
+
if (!parent || parent.nodeClass !== NodeClass.Variable) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
const dataItem = parent as UAVariable;
|
|
80
|
+
const typeDefinition = dataItem.typeDefinitionObj;
|
|
81
|
+
if (!typeDefinition || typeDefinition.nodeClass !== NodeClass.VariableType) {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
const dataItemType = dataItem.addressSpace.findNode(dataItemTypeNodeId) as UAVariableType | null;
|
|
85
|
+
if (!dataItemType || !typeDefinition.isSubtypeOf(dataItemType)) {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
cache.$$semanticsOwner = dataItem;
|
|
89
|
+
return dataItem;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* called whenever a Variable's value really changed: if that Variable is a semantics-bearing
|
|
94
|
+
* Property of a DataItem, the DataItem's semantic_version is bumped so that the next data
|
|
95
|
+
* change notification of the DataItem carries the SemanticsChanged bit.
|
|
96
|
+
*/
|
|
97
|
+
export function notifySemanticsChangedIfNeeded(property: UAVariable): void {
|
|
98
|
+
const dataItem = semanticsOwnerOf(property);
|
|
99
|
+
if (dataItem) {
|
|
100
|
+
(dataItem as UAVariable & { handle_semantic_changed: (dataValue?: unknown) => void }).handle_semantic_changed();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -128,8 +128,6 @@ export function promoteToMultiStateDiscrete<T, DT extends DataType>(node: UAVari
|
|
|
128
128
|
_node._post_initialize();
|
|
129
129
|
|
|
130
130
|
assert(_node.enumStrings.browseName.toString() === "EnumStrings");
|
|
131
|
-
const handler = _node.handle_semantic_changed.bind(_node);
|
|
132
|
-
_node.enumStrings.on("value_changed", handler);
|
|
133
131
|
_node.install_extra_properties();
|
|
134
132
|
return node as UAMultiStateDiscreteImpl<T, DT>;
|
|
135
133
|
}
|
|
@@ -32,13 +32,10 @@ export class UATwoStateDiscreteImplBase extends UAVariableImplT<boolean, DataTyp
|
|
|
32
32
|
_post_initialize(): void {
|
|
33
33
|
// The StatusCode SemanticsChanged bit shall be set if any of the FalseState or TrueState
|
|
34
34
|
// (changes can cause misinterpretation by users or (scripting) programs) Properties are changed
|
|
35
|
-
// (see section 5.2 for additional information).
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
// (see section 5.2 for additional information). That is done generically in
|
|
36
|
+
// impl/data_access/semantics_changed.ts; all this has to do is warn about a missing Property.
|
|
38
37
|
const falseState = this.getPropertyByName("FalseState");
|
|
39
|
-
if (falseState) {
|
|
40
|
-
falseState.on("value_changed", handler);
|
|
41
|
-
} else {
|
|
38
|
+
if (!falseState) {
|
|
42
39
|
/* c8 ignore next */
|
|
43
40
|
console.warn(
|
|
44
41
|
"warning: UATwoStateDiscrete -> a FalseState property is mandatory ",
|
|
@@ -47,9 +44,7 @@ export class UATwoStateDiscreteImplBase extends UAVariableImplT<boolean, DataTyp
|
|
|
47
44
|
);
|
|
48
45
|
}
|
|
49
46
|
const trueState = this.getPropertyByName("TrueState");
|
|
50
|
-
if (trueState) {
|
|
51
|
-
trueState.on("value_changed", handler);
|
|
52
|
-
} else {
|
|
47
|
+
if (!trueState) {
|
|
53
48
|
/* c8 ignore next */
|
|
54
49
|
console.warn(
|
|
55
50
|
"waring: UATwoStateDiscrete -> a TrueState property is mandatory",
|
|
@@ -154,11 +149,10 @@ export function _addTwoStateDiscrete(namespace: INamespace, options: AddTwoState
|
|
|
154
149
|
const dataValueVerif = variable.readValue();
|
|
155
150
|
assert(dataValueVerif.value.dataType === DataType.Boolean);
|
|
156
151
|
*/
|
|
157
|
-
const handler = (variable as UAVariableImpl).handle_semantic_changed.bind(variable);
|
|
158
152
|
|
|
159
153
|
add_dataItem_stuff(variable, options);
|
|
160
154
|
|
|
161
|
-
|
|
155
|
+
namespace.addVariable({
|
|
162
156
|
browseName: { name: "TrueState", namespaceIndex: 0 },
|
|
163
157
|
dataType: "LocalizedText",
|
|
164
158
|
minimumSamplingInterval: 0,
|
|
@@ -171,9 +165,7 @@ export function _addTwoStateDiscrete(namespace: INamespace, options: AddTwoState
|
|
|
171
165
|
})
|
|
172
166
|
});
|
|
173
167
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
const falseStateNode = namespace.addVariable({
|
|
168
|
+
namespace.addVariable({
|
|
177
169
|
browseName: { name: "FalseState", namespaceIndex: 0 },
|
|
178
170
|
dataType: "LocalizedText",
|
|
179
171
|
minimumSamplingInterval: 0,
|
|
@@ -187,8 +179,6 @@ export function _addTwoStateDiscrete(namespace: INamespace, options: AddTwoState
|
|
|
187
179
|
})
|
|
188
180
|
});
|
|
189
181
|
|
|
190
|
-
falseStateNode.on("value_changed", handler);
|
|
191
|
-
|
|
192
182
|
variable.install_extra_properties();
|
|
193
183
|
|
|
194
184
|
return promoteToTwoStateDiscrete(variable);
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The supported way to plug a ReadProcessedDetails implementation into an address space.
|
|
3
|
+
*
|
|
4
|
+
* The aggregate functions live in node-opcua-aggregates, and the address space calls into them
|
|
5
|
+
* when a client issues a ReadProcessed history request, so the two packages need a hook between
|
|
6
|
+
* them. It used to be installed by casting the address space to AddressSpacePrivate and
|
|
7
|
+
* assigning to `_readProcessedDetails` from outside, which made an underscore-prefixed field
|
|
8
|
+
* part of another package's build in all but name.
|
|
9
|
+
*/
|
|
10
|
+
import type { ContinuationData, IAddressSpace, ISessionContext, UAVariable } from "node-opcua-address-space-base";
|
|
11
|
+
import type { QualifiedNameLike } from "node-opcua-data-model";
|
|
12
|
+
import type { NumericRange } from "node-opcua-numeric-range";
|
|
13
|
+
import type { CallbackT } from "node-opcua-status-code";
|
|
14
|
+
import type { HistoryReadResult, ReadProcessedDetails } from "node-opcua-types";
|
|
15
|
+
import type { AddressSpacePrivate } from "../address_space_private.js";
|
|
16
|
+
|
|
17
|
+
/** answers a ReadProcessed history request for one variable */
|
|
18
|
+
export type ReadProcessedDetailsFunc = (
|
|
19
|
+
variable: UAVariable,
|
|
20
|
+
context: ISessionContext,
|
|
21
|
+
historyReadDetails: ReadProcessedDetails,
|
|
22
|
+
indexRange: NumericRange | null,
|
|
23
|
+
dataEncoding: QualifiedNameLike | null,
|
|
24
|
+
continuationData: ContinuationData,
|
|
25
|
+
callback: CallbackT<HistoryReadResult>
|
|
26
|
+
) => void;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Install the handler an address space calls for ReadProcessed history requests.
|
|
30
|
+
*
|
|
31
|
+
* Without one, a ReadProcessed request answers BadHistoryOperationUnsupported.
|
|
32
|
+
*/
|
|
33
|
+
export function installReadProcessedDetails(addressSpace: IAddressSpace, handler: ReadProcessedDetailsFunc): void {
|
|
34
|
+
(addressSpace as AddressSpacePrivate)._readProcessedDetails = handler;
|
|
35
|
+
}
|
package/impl/namespace_impl.ts
CHANGED
|
@@ -1036,8 +1036,8 @@ export class NamespaceImpl implements NamespacePrivate {
|
|
|
1036
1036
|
|
|
1037
1037
|
assert(euRange.readValue().value.value instanceof Range);
|
|
1038
1038
|
|
|
1039
|
-
|
|
1040
|
-
|
|
1039
|
+
// the SemanticsChanged wiring of EURange/InstrumentRange/EngineeringUnits is generic and
|
|
1040
|
+
// lives in impl/data_access/semantics_changed.ts: it covers DataItems built any other way too.
|
|
1041
1041
|
|
|
1042
1042
|
if (Object.hasOwn(options, "instrumentRange")) {
|
|
1043
1043
|
const instrumentRangeExisting = variable.getPropertyByName("InstrumentRange") as UAVariableImpl | null;
|
|
@@ -1062,8 +1062,6 @@ export class NamespaceImpl implements NamespacePrivate {
|
|
|
1062
1062
|
StatusCodes.Good
|
|
1063
1063
|
);
|
|
1064
1064
|
}
|
|
1065
|
-
|
|
1066
|
-
instrumentRange.on("value_changed", handler);
|
|
1067
1065
|
}
|
|
1068
1066
|
(variable as unknown as { acceptValueOutOfRange?: boolean }).acceptValueOutOfRange = options.acceptValueOutOfRange;
|
|
1069
1067
|
|
|
@@ -1096,8 +1094,6 @@ export class NamespaceImpl implements NamespacePrivate {
|
|
|
1096
1094
|
StatusCodes.Good
|
|
1097
1095
|
);
|
|
1098
1096
|
}
|
|
1099
|
-
|
|
1100
|
-
eu.on("value_changed", handler);
|
|
1101
1097
|
}
|
|
1102
1098
|
|
|
1103
1099
|
variable.install_extra_properties();
|
package/impl/nodeid_manager.ts
CHANGED
|
@@ -51,11 +51,17 @@ function _filterAggregates(addressSpace: AddressSpacePartial, references: UARefe
|
|
|
51
51
|
return [ref.nodeId, ""];
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
function _declaredParent(options: ConstructNodeIdOptions): NodeId | null {
|
|
55
|
+
const declared = options.parentNodeId;
|
|
56
|
+
if (!declared) return null;
|
|
57
|
+
if (declared instanceof BaseNodeImpl) return declared.nodeId;
|
|
58
|
+
const nodeId = resolveNodeId(declared as NodeIdLike);
|
|
59
|
+
return nodeId.isEmpty() ? null : nodeId;
|
|
60
|
+
}
|
|
61
|
+
|
|
54
62
|
function _findParentNodeId(addressSpace: AddressSpacePartial, options: ConstructNodeIdOptions): [NodeId, Suffix] | null {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
for (const ref of options.references) {
|
|
63
|
+
const references = options.references ?? [];
|
|
64
|
+
for (const ref of references) {
|
|
59
65
|
const _ref = ref as ReferenceImpl;
|
|
60
66
|
_ref._referenceType = addressSpace.findReferenceType(ref.referenceType) ?? undefined;
|
|
61
67
|
/* c8 ignore next */
|
|
@@ -64,8 +70,19 @@ function _findParentNodeId(addressSpace: AddressSpacePartial, options: Construct
|
|
|
64
70
|
}
|
|
65
71
|
_ref.referenceType = _ref._referenceType.nodeId;
|
|
66
72
|
}
|
|
73
|
+
// A declared parent names the node, whatever reference links the two, as it
|
|
74
|
+
// is the node's parent: an Organizes-only member of a type is
|
|
75
|
+
// `<Type>_MachineryBuildingBlocks` in the ModelCompiler's NodeIds.csv, and a
|
|
76
|
+
// node with two aggregating parents is named after the declared one.
|
|
77
|
+
const declared = _declaredParent(options);
|
|
78
|
+
if (declared) {
|
|
79
|
+
const viaEncoding = references.some(
|
|
80
|
+
(ref) => !ref.isForward && sameNodeId(ref.nodeId, declared) && sameNodeId(ref.referenceType, hasEncoding)
|
|
81
|
+
);
|
|
82
|
+
return [declared, viaEncoding ? "_Encoding" : ""];
|
|
83
|
+
}
|
|
67
84
|
// find HasComponent, or has Property reverse
|
|
68
|
-
return _filterAggregates(addressSpace,
|
|
85
|
+
return references.length ? _filterAggregates(addressSpace, references) : null;
|
|
69
86
|
}
|
|
70
87
|
|
|
71
88
|
function prepareName(browseName?: QualifiedName | QualifiedNameOptions): string {
|
|
@@ -83,6 +100,11 @@ export interface ConstructNodeIdOptions {
|
|
|
83
100
|
nodeClass?: NodeClass;
|
|
84
101
|
references?: UAReference[];
|
|
85
102
|
registerSymbolicNames?: boolean;
|
|
103
|
+
/**
|
|
104
|
+
* The declared parent (NodeSet `ParentNodeId`, or `parentNodeId` on the add* API).
|
|
105
|
+
* When present it names the node, as it is the node's parent (see `BaseNode.parent`).
|
|
106
|
+
*/
|
|
107
|
+
parentNodeId?: NodeIdLike | BaseNode | null;
|
|
86
108
|
}
|
|
87
109
|
export type NodeEntry = [string, number, NodeClass];
|
|
88
110
|
export type NodeEntry1 = [string, number, string /*"Object" | "Variable" etc...*/];
|
package/impl/ua_object_impl.ts
CHANGED
|
@@ -77,6 +77,10 @@ export class UAObjectImpl<T extends UAObjectEvents & ListenerSignature<T> = UAOb
|
|
|
77
77
|
this._eventNotifier = options.eventNotifier || EventNotifierFlags.None;
|
|
78
78
|
assert(typeof this.eventNotifier === "number" && isValidByte(this.eventNotifier));
|
|
79
79
|
this.symbolicName = options.symbolicName || null;
|
|
80
|
+
// every event MonitoredItem on this object is one "event" listener, and the Server
|
|
81
|
+
// object collects them for every Session: more than ten is the normal case, not a
|
|
82
|
+
// leak for Node to warn about (UAVariableImpl does the same for its "value_changed")
|
|
83
|
+
this.setMaxListeners(5000);
|
|
80
84
|
}
|
|
81
85
|
|
|
82
86
|
public readAttribute(
|
|
@@ -275,4 +279,12 @@ export class UAObjectImpl<T extends UAObjectEvents & ListenerSignature<T> = UAOb
|
|
|
275
279
|
}
|
|
276
280
|
}
|
|
277
281
|
|
|
278
|
-
|
|
282
|
+
// A colourless chalk for `inspect(node, { colors: false })`. Not a Proxy over `chalk`
|
|
283
|
+
// returning the identity for every property: chalk 4 defines each style (`cyan`, ...)
|
|
284
|
+
// on first use as a non-writable, non-configurable own data property, after which a
|
|
285
|
+
// Proxy `get` trap returning anything else violates the Proxy invariant and throws a
|
|
286
|
+
// TypeError. Node's own `warnMaxListenersExceeded` inspects the emitter without
|
|
287
|
+
// colours, so that TypeError surfaced inside `node.on("event", ...)` when an eleventh
|
|
288
|
+
// event MonitoredItem was created on the Server object, and killed the server process
|
|
289
|
+
// (CTT A and C Refresh Err_004).
|
|
290
|
+
const plainChalk = new chalk.Instance({ level: 0 });
|
|
@@ -147,6 +147,7 @@ export class UAObjectTypeImpl extends BaseNodeImpl<BaseNodeEvents> implements UA
|
|
|
147
147
|
eventSourceOf: options.eventSourceOf,
|
|
148
148
|
notifierOf: options.notifierOf,
|
|
149
149
|
organizedBy: options.organizedBy,
|
|
150
|
+
parentNodeId: options.parentNodeId,
|
|
150
151
|
addInOf: options.addInOf,
|
|
151
152
|
references,
|
|
152
153
|
|
package/impl/ua_variable_impl.ts
CHANGED
|
@@ -84,6 +84,7 @@ import { apply_condition_refresh, type ConditionRefreshCache } from "./apply_con
|
|
|
84
84
|
import { BaseNodeImpl, type InternalBaseNodeOptions } from "./base_node_impl.js";
|
|
85
85
|
import { _clone, ToStringBuilder, UAVariable_toString, valueRankToString } from "./base_node_private.js";
|
|
86
86
|
import { adjustDataValueStatusCode } from "./data_access/adjust_datavalue_status_code.js";
|
|
87
|
+
import { notifySemanticsChangedIfNeeded } from "./data_access/semantics_changed.js";
|
|
87
88
|
import { _getBasicDataType } from "./get_basic_datatype.js";
|
|
88
89
|
import { type EnumerationInfo, type IEnumItem, UADataTypeImpl } from "./ua_data_type_impl.js";
|
|
89
90
|
import {
|
|
@@ -104,7 +105,8 @@ const warningLog = make_warningLog("ua_variable_impl");
|
|
|
104
105
|
const doDebug = checkDebugFlag("ua_variable_impl");
|
|
105
106
|
const errorLog = make_errorLog("ua_variable_impl");
|
|
106
107
|
|
|
107
|
-
|
|
108
|
+
// a colourless chalk; see ua_object_impl.ts for why it is not a Proxy over `chalk`
|
|
109
|
+
const plainChalk = new chalk.Instance({ level: 0 });
|
|
108
110
|
|
|
109
111
|
export function adjust_accessLevel(accessLevel: string | number | AccessLevelFlag | null | undefined): AccessLevelFlag {
|
|
110
112
|
const flag = makeAccessLevelFlag(accessLevel ?? "CurrentRead | CurrentWrite");
|
|
@@ -1963,6 +1965,15 @@ export class UAVariableImpl<T extends UAVariableEvents & ListenerSignature<T> =
|
|
|
1963
1965
|
}
|
|
1964
1966
|
|
|
1965
1967
|
if (!sameDataValue(old_dataValue, dataValue)) {
|
|
1968
|
+
// a Property that carries the semantics of a DataItem (EURange, TrueState, ...) has to make
|
|
1969
|
+
// the DataItem's next notification carry the SemanticsChanged bit. Done from the Property
|
|
1970
|
+
// rather than with a listener installed when the DataItem is built, because a hand-built
|
|
1971
|
+
// DataItem gets its Properties after itself - see impl/data_access/semantics_changed.ts.
|
|
1972
|
+
// ... but a Property receiving its first value is a Variable being built, not a
|
|
1973
|
+
// semantics change: until then its StatusCode is UncertainInitialValue.
|
|
1974
|
+
if (old_dataValue.statusCode.isGood()) {
|
|
1975
|
+
notifySemanticsChangedIfNeeded(this as UAVariable);
|
|
1976
|
+
}
|
|
1966
1977
|
if (this.getBasicDataType() === DataType.ExtensionObject) {
|
|
1967
1978
|
const preciseClock = coerceClock(this.$dataValue.sourceTimestamp, this.$dataValue.sourcePicoseconds);
|
|
1968
1979
|
const cache: Set<UAVariable> = new Set();
|
|
@@ -331,6 +331,7 @@ export class UAVariableTypeImpl extends BaseNodeImpl<BaseNodeEvents> implements
|
|
|
331
331
|
nodeId: options.nodeId,
|
|
332
332
|
notifierOf: options.notifierOf,
|
|
333
333
|
organizedBy: options.organizedBy,
|
|
334
|
+
parentNodeId: options.parentNodeId,
|
|
334
335
|
// see InstantiateOptions.references: created with the node so the
|
|
335
336
|
// NodeIdManager derives the symbolic name from that parent
|
|
336
337
|
references: options.references,
|