node-opcua-address-space 2.90.0 → 2.91.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 (80) hide show
  1. package/dist/source/helpers/argument_list.js +3 -3
  2. package/dist/source/helpers/argument_list.js.map +1 -1
  3. package/dist/source/interfaces/state_machine/ua_state_machine_type.d.ts +13 -1
  4. package/dist/source/loader/load_nodeset2.js +5 -5
  5. package/dist/source/loader/load_nodeset2.js.map +1 -1
  6. package/dist/src/address_space.js +8 -8
  7. package/dist/src/address_space.js.map +1 -1
  8. package/dist/src/alarms_and_conditions/ua_alarm_condition_impl.js +1 -1
  9. package/dist/src/alarms_and_conditions/ua_alarm_condition_impl.js.map +1 -1
  10. package/dist/src/alarms_and_conditions/ua_condition_impl.js +2 -2
  11. package/dist/src/alarms_and_conditions/ua_condition_impl.js.map +1 -1
  12. package/dist/src/alarms_and_conditions/ua_discrete_alarm_impl.js +1 -1
  13. package/dist/src/alarms_and_conditions/ua_discrete_alarm_impl.js.map +1 -1
  14. package/dist/src/base_node_impl.js +3 -3
  15. package/dist/src/base_node_impl.js.map +1 -1
  16. package/dist/src/base_node_private.js +4 -4
  17. package/dist/src/base_node_private.js.map +1 -1
  18. package/dist/src/check_value_rank_compatibility.d.ts +15 -0
  19. package/dist/src/check_value_rank_compatibility.js +82 -0
  20. package/dist/src/check_value_rank_compatibility.js.map +1 -0
  21. package/dist/src/extension_object_array_node.js +3 -3
  22. package/dist/src/extension_object_array_node.js.map +1 -1
  23. package/dist/src/index_current.d.ts +1 -1
  24. package/dist/src/index_current.js +2 -1
  25. package/dist/src/index_current.js.map +1 -1
  26. package/dist/src/namespace_impl.js +0 -2
  27. package/dist/src/namespace_impl.js.map +1 -1
  28. package/dist/src/nodeset_tools/nodeset_to_xml.js +9 -9
  29. package/dist/src/nodeset_tools/nodeset_to_xml.js.map +1 -1
  30. package/dist/src/state_machine/finite_state_machine.d.ts +16 -2
  31. package/dist/src/state_machine/finite_state_machine.js +83 -44
  32. package/dist/src/state_machine/finite_state_machine.js.map +1 -1
  33. package/dist/src/tool_isSubtypeOf.d.ts +18 -0
  34. package/dist/src/tool_isSubtypeOf.js +125 -0
  35. package/dist/src/tool_isSubtypeOf.js.map +1 -0
  36. package/dist/src/tool_isSupertypeOf.d.ts +4 -4
  37. package/dist/src/ua_data_type_impl.d.ts +6 -4
  38. package/dist/src/ua_data_type_impl.js +13 -11
  39. package/dist/src/ua_data_type_impl.js.map +1 -1
  40. package/dist/src/ua_object_type_impl.d.ts +4 -2
  41. package/dist/src/ua_object_type_impl.js +7 -5
  42. package/dist/src/ua_object_type_impl.js.map +1 -1
  43. package/dist/src/ua_reference_type_impl.d.ts +5 -3
  44. package/dist/src/ua_reference_type_impl.js +8 -6
  45. package/dist/src/ua_reference_type_impl.js.map +1 -1
  46. package/dist/src/ua_variable_impl.d.ts +19 -7
  47. package/dist/src/ua_variable_impl.js +38 -23
  48. package/dist/src/ua_variable_impl.js.map +1 -1
  49. package/dist/src/ua_variable_impl_ext_obj.js +22 -13
  50. package/dist/src/ua_variable_impl_ext_obj.js.map +1 -1
  51. package/dist/src/ua_variable_type_impl.d.ts +15 -3
  52. package/dist/src/ua_variable_type_impl.js +17 -6
  53. package/dist/src/ua_variable_type_impl.js.map +1 -1
  54. package/distHelpers/boiler_system.js +5 -5
  55. package/distHelpers/boiler_system.js.map +1 -1
  56. package/package.json +34 -34
  57. package/source/helpers/argument_list.ts +3 -3
  58. package/source/interfaces/state_machine/ua_state_machine_type.ts +19 -1
  59. package/source/loader/load_nodeset2.ts +11 -10
  60. package/src/address_space.ts +8 -8
  61. package/src/alarms_and_conditions/ua_alarm_condition_impl.ts +1 -1
  62. package/src/alarms_and_conditions/ua_condition_impl.ts +2 -2
  63. package/src/alarms_and_conditions/ua_discrete_alarm_impl.ts +1 -1
  64. package/src/base_node_impl.ts +3 -3
  65. package/src/base_node_private.ts +3 -3
  66. package/src/check_value_rank_compatibility.ts +79 -0
  67. package/src/extension_object_array_node.ts +3 -3
  68. package/src/index_current.ts +1 -1
  69. package/src/namespace_impl.ts +0 -4
  70. package/src/nodeset_tools/nodeset_to_xml.ts +9 -9
  71. package/src/state_machine/finite_state_machine.ts +105 -60
  72. package/src/{tool_isSupertypeOf.ts → tool_isSubtypeOf.ts} +9 -9
  73. package/src/ua_data_type_impl.ts +21 -19
  74. package/src/ua_object_type_impl.ts +9 -3
  75. package/src/ua_reference_type_impl.ts +7 -4
  76. package/src/ua_variable_impl.ts +2033 -2006
  77. package/src/ua_variable_impl_ext_obj.ts +28 -15
  78. package/src/ua_variable_type_impl.ts +31 -5
  79. package/test_helpers/boiler_system.ts +5 -5
  80. package/test_helpers/test_fixtures/eurange_issue.xml +3 -2
@@ -1,2272 +1,2299 @@
1
- /* eslint-disable max-statements */
2
- /* eslint-disable complexity */
3
- /**
4
- * @module node-opcua-address-space
5
- */
6
- // tslint:disable:no-bitwise
7
- // tslint:disable:no-console
8
- // tslint:disable:max-line-length
9
- import * as chalk from "chalk";
10
-
11
- import {
12
- BindExtensionObjectOptions,
13
- CloneExtraInfo,
14
- ContinuationData,
15
- defaultCloneExtraInfo,
16
- defaultCloneFilter,
17
- GetFunc,
18
- SetFunc,
19
- VariableDataValueGetterSync,
20
- VariableDataValueSetterWithCallback
21
- } from "node-opcua-address-space-base";
22
- import { assert } from "node-opcua-assert";
23
- import {
24
- isValidDataEncoding,
25
- convertAccessLevelFlagToByte,
26
- QualifiedNameLike,
27
- NodeClass,
28
- AccessLevelFlag,
29
- makeAccessLevelFlag,
30
- AttributeIds,
31
- isDataEncoding,
32
- QualifiedName
33
- } from "node-opcua-data-model";
34
- import { extractRange, sameDataValue, DataValue, DataValueLike, DataValueT } from "node-opcua-data-value";
35
- import { coerceClock, getCurrentClock, PreciseClock } from "node-opcua-date-time";
36
- import { checkDebugFlag, make_debugLog, make_errorLog, make_warningLog } from "node-opcua-debug";
37
- import { ExtensionObject } from "node-opcua-extension-object";
38
- import { NodeId } from "node-opcua-nodeid";
39
- import { NumericRange } from "node-opcua-numeric-range";
40
- import { WriteValue } from "node-opcua-service-write";
41
- import { StatusCode, StatusCodes, CallbackT } from "node-opcua-status-code";
42
- import {
43
- HistoryReadDetails,
44
- HistoryReadResult,
45
- PermissionType,
46
- ReadAtTimeDetails,
47
- ReadEventDetails,
48
- ReadProcessedDetails,
49
- ReadRawModifiedDetails,
50
- WriteValueOptions
51
- } from "node-opcua-types";
52
- import * as utils from "node-opcua-utils";
53
- import {
54
- Variant,
55
- VariantLike,
56
- DataType,
57
- sameVariant,
58
- VariantArrayType,
59
- adjustVariant,
60
- verifyRankAndDimensions
61
- } from "node-opcua-variant";
62
- import { StatusCodeCallback } from "node-opcua-status-code";
63
- import {
64
- IAddressSpace,
65
- BindVariableOptions,
66
- IVariableHistorian,
67
- TimestampGetFunc,
68
- TimestampSetFunc,
69
- UADataType,
70
- UAVariable,
71
- UAVariableType,
72
- CloneOptions,
73
- CloneFilter,
74
- ISessionContext,
75
- BaseNode,
76
- UAVariableT
77
- } from "node-opcua-address-space-base";
78
- import { UAHistoricalDataConfiguration } from "node-opcua-nodeset-ua";
79
-
80
- import { SessionContext } from "../source/session_context";
81
- import { convertToCallbackFunction1 } from "../source/helpers/multiform_func";
82
- import { BaseNodeImpl, InternalBaseNodeOptions } from "./base_node_impl";
83
- import { _clone, ToStringBuilder, UAVariable_toString } from "./base_node_private";
84
- import { EnumerationInfo, IEnumItem, UADataTypeImpl } from "./ua_data_type_impl";
85
- import { apply_condition_refresh, ConditionRefreshCache } from "./apply_condition_refresh";
86
- import {
87
- extractPartialData,
88
- incrementElement,
89
- propagateTouchValueDownward,
90
- propagateTouchValueDownwardArray,
91
- propagateTouchValueUpward,
92
- setExtensionObjectPartialValue,
93
- _bindExtensionObject,
94
- _bindExtensionObjectArrayOrMatrix,
95
- _installExtensionObjectBindingOnProperties,
96
- _touchValue
97
- } from "./ua_variable_impl_ext_obj";
98
-
99
- const debugLog = make_debugLog(__filename);
100
- const warningLog = make_warningLog(__filename);
101
- const doDebug = checkDebugFlag(__filename);
102
- const errorLog = make_errorLog(__filename);
103
-
104
-
105
-
106
- export function adjust_accessLevel(accessLevel: string | number | null): AccessLevelFlag {
107
- accessLevel = utils.isNullOrUndefined(accessLevel) ? "CurrentRead | CurrentWrite" : accessLevel;
108
- accessLevel = makeAccessLevelFlag(accessLevel);
109
- assert(isFinite(accessLevel));
110
- return accessLevel;
111
- }
112
-
113
- export function adjust_userAccessLevel(
114
- userAccessLevel: string | number | null | undefined,
115
- accessLevel: string | number | null
116
- ): AccessLevelFlag | undefined {
117
- if (userAccessLevel === undefined) {
118
- return undefined;
119
- }
120
- userAccessLevel = adjust_accessLevel(userAccessLevel);
121
- accessLevel = adjust_accessLevel(accessLevel);
122
- return makeAccessLevelFlag(accessLevel & userAccessLevel);
123
- }
124
-
125
- function adjust_samplingInterval(minimumSamplingInterval: number): number {
126
- assert(isFinite(minimumSamplingInterval));
127
- if (minimumSamplingInterval < 0) {
128
- return -1; // only -1 is a valid negative value for samplingInterval and means "unspecified"
1
+ /* eslint-disable max-statements */
2
+ /* eslint-disable complexity */
3
+ /**
4
+ * @module node-opcua-address-space
5
+ */
6
+ // tslint:disable:no-bitwise
7
+ // tslint:disable:no-console
8
+ // tslint:disable:max-line-length
9
+ import * as chalk from "chalk";
10
+
11
+ import {
12
+ BindExtensionObjectOptions,
13
+ CloneExtraInfo,
14
+ ContinuationData,
15
+ defaultCloneExtraInfo,
16
+ defaultCloneFilter,
17
+ GetFunc,
18
+ SetFunc,
19
+ VariableDataValueGetterSync,
20
+ VariableDataValueSetterWithCallback
21
+ } from "node-opcua-address-space-base";
22
+ import { assert } from "node-opcua-assert";
23
+ import {
24
+ isValidDataEncoding,
25
+ convertAccessLevelFlagToByte,
26
+ QualifiedNameLike,
27
+ NodeClass,
28
+ AccessLevelFlag,
29
+ makeAccessLevelFlag,
30
+ AttributeIds,
31
+ isDataEncoding,
32
+ QualifiedName
33
+ } from "node-opcua-data-model";
34
+ import { extractRange, sameDataValue, DataValue, DataValueLike, DataValueT } from "node-opcua-data-value";
35
+ import { coerceClock, getCurrentClock, PreciseClock } from "node-opcua-date-time";
36
+ import { checkDebugFlag, make_debugLog, make_errorLog, make_warningLog } from "node-opcua-debug";
37
+ import { ExtensionObject } from "node-opcua-extension-object";
38
+ import { NodeId } from "node-opcua-nodeid";
39
+ import { NumericRange } from "node-opcua-numeric-range";
40
+ import { WriteValue } from "node-opcua-service-write";
41
+ import { StatusCode, StatusCodes, CallbackT } from "node-opcua-status-code";
42
+ import {
43
+ HistoryReadDetails,
44
+ HistoryReadResult,
45
+ PermissionType,
46
+ ReadAtTimeDetails,
47
+ ReadEventDetails,
48
+ ReadProcessedDetails,
49
+ ReadRawModifiedDetails,
50
+ WriteValueOptions
51
+ } from "node-opcua-types";
52
+ import * as utils from "node-opcua-utils";
53
+ import {
54
+ Variant,
55
+ VariantLike,
56
+ DataType,
57
+ sameVariant,
58
+ VariantArrayType,
59
+ adjustVariant,
60
+ verifyRankAndDimensions
61
+ } from "node-opcua-variant";
62
+ import { StatusCodeCallback } from "node-opcua-status-code";
63
+ import {
64
+ IAddressSpace,
65
+ BindVariableOptions,
66
+ IVariableHistorian,
67
+ TimestampGetFunc,
68
+ TimestampSetFunc,
69
+ UADataType,
70
+ UAVariable,
71
+ UAVariableType,
72
+ CloneOptions,
73
+ CloneFilter,
74
+ ISessionContext,
75
+ BaseNode,
76
+ UAVariableT
77
+ } from "node-opcua-address-space-base";
78
+ import { UAHistoricalDataConfiguration } from "node-opcua-nodeset-ua";
79
+
80
+ import { SessionContext } from "../source/session_context";
81
+ import { convertToCallbackFunction1 } from "../source/helpers/multiform_func";
82
+ import { BaseNodeImpl, InternalBaseNodeOptions } from "./base_node_impl";
83
+ import { _clone, ToStringBuilder, UAVariable_toString } from "./base_node_private";
84
+ import { EnumerationInfo, IEnumItem, UADataTypeImpl } from "./ua_data_type_impl";
85
+ import { apply_condition_refresh, ConditionRefreshCache } from "./apply_condition_refresh";
86
+ import {
87
+ extractPartialData,
88
+ incrementElement,
89
+ propagateTouchValueDownward,
90
+ propagateTouchValueDownwardArray,
91
+ propagateTouchValueUpward,
92
+ setExtensionObjectPartialValue,
93
+ _bindExtensionObject,
94
+ _bindExtensionObjectArrayOrMatrix,
95
+ _installExtensionObjectBindingOnProperties,
96
+ _touchValue
97
+ } from "./ua_variable_impl_ext_obj";
98
+
99
+ const debugLog = make_debugLog(__filename);
100
+ const warningLog = make_warningLog(__filename);
101
+ const doDebug = checkDebugFlag(__filename);
102
+ const errorLog = make_errorLog(__filename);
103
+
104
+
105
+
106
+ export function adjust_accessLevel(accessLevel: string | number | null): AccessLevelFlag {
107
+ accessLevel = utils.isNullOrUndefined(accessLevel) ? "CurrentRead | CurrentWrite" : accessLevel;
108
+ accessLevel = makeAccessLevelFlag(accessLevel);
109
+ assert(isFinite(accessLevel));
110
+ return accessLevel;
111
+ }
112
+
113
+ export function adjust_userAccessLevel(
114
+ userAccessLevel: string | number | null | undefined,
115
+ accessLevel: string | number | null
116
+ ): AccessLevelFlag | undefined {
117
+ if (userAccessLevel === undefined) {
118
+ return undefined;
119
+ }
120
+ userAccessLevel = adjust_accessLevel(userAccessLevel);
121
+ accessLevel = adjust_accessLevel(accessLevel);
122
+ return makeAccessLevelFlag(accessLevel & userAccessLevel);
123
+ }
124
+
125
+ function adjust_samplingInterval(minimumSamplingInterval: number): number {
126
+ assert(isFinite(minimumSamplingInterval));
127
+ if (minimumSamplingInterval < 0) {
128
+ return -1; // only -1 is a valid negative value for samplingInterval and means "unspecified"
129
+ }
130
+ return minimumSamplingInterval;
131
+ }
132
+
133
+ function is_Variant(v: any): boolean {
134
+ return v instanceof Variant;
135
+ }
136
+
137
+ function is_StatusCode(v: any): boolean {
138
+ return (
139
+ v &&
140
+ v.constructor &&
141
+ (v.constructor.name === "ConstantStatusCode" ||
142
+ v.constructor.name === "StatusCode" ||
143
+ v.constructor.name === "ModifiableStatusCode")
144
+ );
129
145
  }
130
- return minimumSamplingInterval;
131
- }
132
-
133
- function is_Variant(v: any): boolean {
134
- return v instanceof Variant;
135
- }
136
-
137
- function is_StatusCode(v: any): boolean {
138
- return (
139
- v &&
140
- v.constructor &&
141
- (v.constructor.name === "ConstantStatusCode" ||
142
- v.constructor.name === "StatusCode" ||
143
- v.constructor.name === "ModifiableStatusCode")
144
- );
145
- }
146
-
147
- function is_Variant_or_StatusCode(v: any): boolean {
148
- if (is_Variant(v)) {
149
- // /@@assert(v.isValid());
146
+
147
+ function is_Variant_or_StatusCode(v: any): boolean {
148
+ if (is_Variant(v)) {
149
+ // /@@assert(v.isValid());
150
+ }
151
+ return is_Variant(v) || is_StatusCode(v);
150
152
  }
151
- return is_Variant(v) || is_StatusCode(v);
152
- }
153
153
 
154
- function _dataType_toUADataType(addressSpace: IAddressSpace, dataType: DataType): UADataType {
155
- assert(addressSpace);
156
- assert(dataType !== DataType.Null);
154
+ function _dataType_toUADataType(addressSpace: IAddressSpace, dataType: DataType): UADataType {
155
+ assert(addressSpace);
156
+ assert(dataType !== DataType.Null);
157
157
 
158
- const dataTypeNode = addressSpace.findDataType(DataType[dataType]);
159
- /* istanbul ignore next */
160
- if (!dataTypeNode) {
161
- throw new Error(" Cannot find DataType " + DataType[dataType] + " in address Space");
162
- }
163
- return dataTypeNode as UADataType;
164
- }
165
- /*=
166
- *
167
- * @param addressSpace
168
- * @param dataTypeNodeId : the nodeId matching the dataType of the destination variable.
169
- * @param variantDataType: the dataType of the variant to write to the destination variable
170
- * @param nodeId
171
- * @return {boolean} true if the variant dataType is compatible with the Variable DataType
172
- */
173
- function validateDataType(
174
- addressSpace: IAddressSpace,
175
- dataTypeNodeId: NodeId,
176
- variantDataType: DataType,
177
- nodeId: NodeId,
178
- allowNulls: boolean
179
- ): boolean {
180
- if (variantDataType === DataType.ExtensionObject) {
181
- return true;
182
- }
183
- if (variantDataType === DataType.Null && allowNulls) {
184
- return true;
185
- }
186
- if (variantDataType === DataType.Null && !allowNulls) {
187
- return false;
188
- }
189
- let builtInType: DataType;
190
- let builtInUADataType: UADataType;
158
+ const dataTypeNode = addressSpace.findDataType(DataType[dataType]);
159
+ /* istanbul ignore next */
160
+ if (!dataTypeNode) {
161
+ throw new Error(" Cannot find DataType " + DataType[dataType] + " in address Space");
162
+ }
163
+ return dataTypeNode as UADataType;
164
+ }
165
+ /*=
166
+ *
167
+ * @param addressSpace
168
+ * @param dataTypeNodeId : the nodeId matching the dataType of the destination variable.
169
+ * @param variantDataType: the dataType of the variant to write to the destination variable
170
+ * @param nodeId
171
+ * @return {boolean} true if the variant dataType is compatible with the Variable DataType
172
+ */
173
+ function validateDataType(
174
+ addressSpace: IAddressSpace,
175
+ dataTypeNodeId: NodeId,
176
+ variantDataType: DataType,
177
+ nodeId: NodeId,
178
+ allowNulls: boolean
179
+ ): boolean {
180
+ if (variantDataType === DataType.ExtensionObject) {
181
+ return true;
182
+ }
183
+ if (variantDataType === DataType.Null && allowNulls) {
184
+ return true;
185
+ }
186
+ if (variantDataType === DataType.Null && !allowNulls) {
187
+ return false;
188
+ }
189
+ let builtInType: DataType;
190
+ let builtInUADataType: UADataType;
191
191
 
192
- const destUADataType = addressSpace.findDataType(dataTypeNodeId)!;
193
- assert(destUADataType instanceof UADataTypeImpl);
192
+ const destUADataType = addressSpace.findDataType(dataTypeNodeId)!;
193
+ assert(destUADataType instanceof UADataTypeImpl);
194
194
 
195
- if (destUADataType.isAbstract || destUADataType.nodeId.namespace !== 0) {
196
- builtInUADataType = destUADataType;
197
- } else {
198
- builtInType = addressSpace.findCorrespondingBasicDataType(destUADataType);
199
- builtInUADataType = addressSpace.findDataType(builtInType)!;
200
- }
201
- assert(builtInUADataType instanceof UADataTypeImpl);
195
+ if (destUADataType.isAbstract || destUADataType.nodeId.namespace !== 0) {
196
+ builtInUADataType = destUADataType;
197
+ } else {
198
+ builtInType = addressSpace.findCorrespondingBasicDataType(destUADataType);
199
+ builtInUADataType = addressSpace.findDataType(builtInType)!;
200
+ }
201
+ assert(builtInUADataType instanceof UADataTypeImpl);
202
202
 
203
- const enumerationUADataType = addressSpace.findDataType("Enumeration");
204
- if (!enumerationUADataType) {
205
- throw new Error("cannot find Enumeration DataType node in standard address space");
206
- }
207
- if (destUADataType.isSupertypeOf(enumerationUADataType)) {
208
- // istanbul ignore next
209
- if (doDebug) {
210
- debugLog("destUADataType.", destUADataType.browseName.toString(), destUADataType.nodeId.toString());
211
- debugLog(
212
- "enumerationUADataType.",
213
- enumerationUADataType.browseName.toString(),
214
- enumerationUADataType.nodeId.toString()
215
- );
203
+ const enumerationUADataType = addressSpace.findDataType("Enumeration");
204
+ if (!enumerationUADataType) {
205
+ throw new Error("cannot find Enumeration DataType node in standard address space");
206
+ }
207
+ if (destUADataType.isSubtypeOf(enumerationUADataType)) {
208
+ // istanbul ignore next
209
+ if (doDebug) {
210
+ debugLog("destUADataType.", destUADataType.browseName.toString(), destUADataType.nodeId.toString());
211
+ debugLog(
212
+ "enumerationUADataType.",
213
+ enumerationUADataType.browseName.toString(),
214
+ enumerationUADataType.nodeId.toString()
215
+ );
216
+ }
217
+ return true;
216
218
  }
217
- return true;
218
- }
219
219
 
220
- // The value supplied for the attribute is not of the same type as the value.
221
- const variantUADataType = _dataType_toUADataType(addressSpace, variantDataType);
222
- assert(variantUADataType instanceof UADataTypeImpl);
220
+ // The value supplied for the attribute is not of the same type as the value.
221
+ const variantUADataType = _dataType_toUADataType(addressSpace, variantDataType);
222
+ assert(variantUADataType instanceof UADataTypeImpl);
223
223
 
224
- const dest_isSuperTypeOf_variant = variantUADataType.isSupertypeOf(builtInUADataType);
224
+ const dest_isSubTypeOf_variant = variantUADataType.isSubtypeOf(builtInUADataType);
225
225
 
226
- /* istanbul ignore next */
227
- if (doDebug) {
228
- if (dest_isSuperTypeOf_variant) {
229
- /* istanbul ignore next*/
230
- debugLog(chalk.green(" ---------- Type match !!! "), " on ", nodeId.toString());
231
- } else {
232
- /* istanbul ignore next*/
233
- debugLog(chalk.red(" ---------- Type mismatch "), " on ", nodeId.toString());
226
+ /* istanbul ignore next */
227
+ if (doDebug) {
228
+ if (dest_isSubTypeOf_variant) {
229
+ /* istanbul ignore next*/
230
+ debugLog(chalk.green(" ---------- Type match !!! "), " on ", nodeId.toString());
231
+ } else {
232
+ /* istanbul ignore next*/
233
+ debugLog(chalk.red(" ---------- Type mismatch "), " on ", nodeId.toString());
234
+ }
235
+ debugLog(chalk.cyan(" Variable data Type is = "), destUADataType.browseName.toString());
236
+ debugLog(chalk.cyan(" which matches basic Type = "), builtInUADataType.browseName.toString());
237
+ debugLog(chalk.yellow(" Actual dataType = "), variantUADataType.browseName.toString());
234
238
  }
235
- debugLog(chalk.cyan(" Variable data Type is = "), destUADataType.browseName.toString());
236
- debugLog(chalk.cyan(" which matches basic Type = "), builtInUADataType.browseName.toString());
237
- debugLog(chalk.yellow(" Actual dataType = "), variantUADataType.browseName.toString());
239
+
240
+ return dest_isSubTypeOf_variant;
241
+ }
242
+
243
+ function default_func(this: UAVariable, dataValue1: DataValue, callback1: CallbackT<StatusCode>) {
244
+ return _default_writable_timestamped_set_func.call(this, dataValue1, callback1);
238
245
  }
239
246
 
240
- return dest_isSuperTypeOf_variant;
241
- }
242
-
243
- function default_func(this: UAVariable, dataValue1: DataValue, callback1: CallbackT<StatusCode>) {
244
- return _default_writable_timestamped_set_func.call(this, dataValue1, callback1);
245
- }
246
-
247
- interface UAVariableOptions extends InternalBaseNodeOptions {
248
- value?: any;
249
- dataType: NodeId | string;
250
- valueRank?: number;
251
- arrayDimensions?: null | number[];
252
- accessLevel?: any;
253
- userAccessLevel?: any;
254
- minimumSamplingInterval?: number; // default -1
255
- historizing?: number;
256
- }
257
-
258
- /**
259
- * A OPCUA Variable Node
260
- *
261
- * @class UAVariable
262
- * @constructor
263
- * @extends BaseNode
264
- * The AccessLevel Attribute is used to indicate how the Value of a Variable can be accessed (read/write) and if it
265
- * contains current and/or historic data. The AccessLevel does not take any user access rights into account,
266
- * i.e. although the Variable is writable this may be restricted to a certain user / user group.
267
- * The AccessLevel is an 8-bit unsigned integer with the structure defined in the following table:
268
- *
269
- * Field Bit Description
270
- * CurrentRead 0 Indicates if the current value is readable
271
- * (0 means not readable, 1 means readable).
272
- * CurrentWrite 1 Indicates if the current value is writable
273
- * (0 means not writable, 1 means writable).
274
- * HistoryRead 2 Indicates if the history of the value is readable
275
- * (0 means not readable, 1 means readable).
276
- * HistoryWrite 3 Indicates if the history of the value is writable (0 means not writable, 1 means writable).
277
- * SemanticChange 4 Indicates if the Variable used as Property generates SemanticChangeEvents (see 9.31).
278
- * Reserved 5:7 Reserved for future use. Shall always be zero.
279
- *
280
- * The first two bits also indicate if a current value of this Variable is available and the second two bits
281
- * indicates if the history of the Variable is available via the OPC UA server.
282
- *
283
- */
284
- export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
285
- public readonly nodeClass = NodeClass.Variable;
286
-
287
- public dataType: NodeId;
288
- private _basicDataType?: DataType;
289
-
290
- public $extensionObject?: any;
291
-
292
- public $historicalDataConfiguration?: UAHistoricalDataConfiguration;
293
- public varHistorian?: IVariableHistorian;
247
+ interface UAVariableOptions extends InternalBaseNodeOptions {
248
+ value?: any;
249
+ dataType: NodeId | string;
250
+ /**
251
+ * This attribute indicates whether the Value attribute of the Variableis an array and how many dimensions the array has.
252
+ * It may have the following values:
253
+ * * n > 1: the Value is an array with the specified number of dimensions.
254
+ * * OneDimension (1): The value is an array with one dimension.
255
+ * * OneOrMoreDimensions (0): The value is an array with one or more dimensions.
256
+ * * Scalar (−1): The value is not an array.
257
+ * * Any (−2): The value can be a scalar or an array with any number of dimensions.
258
+ * * ScalarOrOneDimension (−3): The value can be a scalar or a one dimensional array.
259
+ * * All DataTypes are considered to be scalar, even if they have array-like semantics like ByteString and String.
260
+ */
261
+ valueRank?: number;
262
+ arrayDimensions?: null | number[];
263
+ accessLevel?: any;
264
+ userAccessLevel?: any;
265
+ minimumSamplingInterval?: number; // default -1
266
+ historizing?: number;
267
+ }
294
268
 
295
269
  /**
296
- * @internal @private
270
+ * A OPCUA Variable Node
271
+ *
272
+ * @class UAVariable
273
+ * @constructor
274
+ * @extends BaseNode
275
+ * The AccessLevel Attribute is used to indicate how the Value of a Variable can be accessed (read/write) and if it
276
+ * contains current and/or historic data. The AccessLevel does not take any user access rights into account,
277
+ * i.e. although the Variable is writable this may be restricted to a certain user / user group.
278
+ * The AccessLevel is an 8-bit unsigned integer with the structure defined in the following table:
279
+ *
280
+ * Field Bit Description
281
+ * CurrentRead 0 Indicates if the current value is readable
282
+ * (0 means not readable, 1 means readable).
283
+ * CurrentWrite 1 Indicates if the current value is writable
284
+ * (0 means not writable, 1 means writable).
285
+ * HistoryRead 2 Indicates if the history of the value is readable
286
+ * (0 means not readable, 1 means readable).
287
+ * HistoryWrite 3 Indicates if the history of the value is writable (0 means not writable, 1 means writable).
288
+ * SemanticChange 4 Indicates if the Variable used as Property generates SemanticChangeEvents (see 9.31).
289
+ * Reserved 5:7 Reserved for future use. Shall always be zero.
290
+ *
291
+ * The first two bits also indicate if a current value of this Variable is available and the second two bits
292
+ * indicates if the history of the Variable is available via the OPC UA server.
293
+ *
297
294
  */
298
- public $dataValue: DataValue;
299
- public accessLevel: number;
300
- public userAccessLevel?: number;
301
- public valueRank: number;
302
- public minimumSamplingInterval: number;
303
- public historizing: boolean;
304
- public semantic_version: number;
305
- public arrayDimensions: null | number[];
306
-
307
- public _timestamped_get_func?: TimestampGetFunc | null;
308
- public _timestamped_set_func?: VariableDataValueSetterWithCallback | null;
309
- public _get_func: any;
310
- public _set_func: any;
311
- public refreshFunc?: (callback: CallbackT<DataValue>) => void;
312
- public __waiting_callbacks?: any[];
313
-
314
- get typeDefinitionObj(): UAVariableType {
315
- // istanbul ignore next
316
- if (super.typeDefinitionObj && super.typeDefinitionObj.nodeClass !== NodeClass.VariableType) {
317
- throw new Error(
318
- "Invalid type definition node class , expecting a VariableType got " + NodeClass[super.typeDefinitionObj.nodeClass]
319
- );
295
+ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
296
+ public readonly nodeClass = NodeClass.Variable;
297
+
298
+ public dataType: NodeId;
299
+ private _basicDataType?: DataType;
300
+
301
+ public $extensionObject?: any;
302
+ public $set_ExtensionObject?: (newValue: ExtensionObject, sourceTimestamp: PreciseClock, cache: Set<UAVariableImpl>) => void;
303
+
304
+ public $historicalDataConfiguration?: UAHistoricalDataConfiguration;
305
+ public varHistorian?: IVariableHistorian;
306
+
307
+ /**
308
+ * @internal @private
309
+ */
310
+ public $dataValue: DataValue;
311
+ public accessLevel: number;
312
+ public userAccessLevel?: number;
313
+ public valueRank: number;
314
+ public minimumSamplingInterval: number;
315
+ public historizing: boolean;
316
+ public semantic_version: number;
317
+ public arrayDimensions: null | number[];
318
+
319
+ public _timestamped_get_func?: TimestampGetFunc | null;
320
+ public _timestamped_set_func?: VariableDataValueSetterWithCallback | null;
321
+ public _get_func: any;
322
+ public _set_func: any;
323
+ public refreshFunc?: (callback: CallbackT<DataValue>) => void;
324
+ public __waiting_callbacks?: any[];
325
+
326
+ get typeDefinitionObj(): UAVariableType {
327
+ // istanbul ignore next
328
+ if (super.typeDefinitionObj && super.typeDefinitionObj.nodeClass !== NodeClass.VariableType) {
329
+ console.log(super.typeDefinitionObj.toString());
330
+ throw new Error(
331
+ "Invalid type definition node class , expecting a VariableType got " + NodeClass[super.typeDefinitionObj.nodeClass]
332
+ );
333
+ }
334
+ return super.typeDefinitionObj as UAVariableType;
320
335
  }
321
- return super.typeDefinitionObj as UAVariableType;
322
- }
323
- get typeDefinition(): NodeId {
324
- return super.typeDefinition;
325
- }
326
- constructor(options: UAVariableOptions) {
327
- super(options);
336
+ get typeDefinition(): NodeId {
337
+ return super.typeDefinition;
338
+ }
339
+ constructor(options: UAVariableOptions) {
340
+ super(options);
328
341
 
329
- verifyRankAndDimensions(options);
330
- this.valueRank = options.valueRank!;
331
- this.arrayDimensions = options.arrayDimensions!;
342
+ verifyRankAndDimensions(options);
343
+ this.valueRank = options.valueRank!;
344
+ this.arrayDimensions = options.arrayDimensions!;
332
345
 
333
- this.dataType = this.resolveNodeId(options.dataType); // DataType (NodeId)
346
+ this.dataType = this.resolveNodeId(options.dataType); // DataType (NodeId)
334
347
 
335
- this.accessLevel = adjust_accessLevel(options.accessLevel);
348
+ this.accessLevel = adjust_accessLevel(options.accessLevel);
336
349
 
337
- this.userAccessLevel = adjust_userAccessLevel(options.userAccessLevel, this.accessLevel);
350
+ this.userAccessLevel = adjust_userAccessLevel(options.userAccessLevel, this.accessLevel);
338
351
 
339
- this.minimumSamplingInterval = adjust_samplingInterval(options.minimumSamplingInterval || 0);
352
+ this.minimumSamplingInterval = adjust_samplingInterval(options.minimumSamplingInterval || 0);
340
353
 
341
- this.historizing = !!options.historizing; // coerced to boolean"
354
+ this.historizing = !!options.historizing; // coerced to boolean"
342
355
 
343
- this.$dataValue = new DataValue({ statusCode: StatusCodes.UncertainInitialValue, value: { dataType: DataType.Null } });
356
+ this.$dataValue = new DataValue({ statusCode: StatusCodes.UncertainInitialValue, value: { dataType: DataType.Null } });
344
357
 
345
- if (options.value) {
346
- this.bindVariable(options.value);
347
- }
358
+ if (options.value) {
359
+ this.bindVariable(options.value);
360
+ }
348
361
 
349
- this.setMaxListeners(5000);
362
+ this.setMaxListeners(5000);
350
363
 
351
- this.semantic_version = 0;
352
- }
353
- private checkAccessLevelPrivate(
354
- _context: ISessionContext,
355
- accessLevel: AccessLevelFlag): boolean {
356
- if (this.userAccessLevel === undefined) {
357
- return true;
364
+ this.semantic_version = 0;
358
365
  }
359
- return (this.userAccessLevel & accessLevel) === accessLevel;
360
- }
361
- private checkPermissionPrivate(
362
- context: ISessionContext,
363
- permission: PermissionType,
364
- ): boolean {
365
- if (!context) return true;
366
- assert(context instanceof SessionContext);
367
- if (context.checkPermission) {
368
- if (!(context.checkPermission instanceof Function)) {
369
- errorLog("context checkPermission is not a function");
370
- return false;
371
- }
372
- if (!context.checkPermission(this, permission)) {
373
- return false;
366
+ private checkAccessLevelPrivate(
367
+ _context: ISessionContext,
368
+ accessLevel: AccessLevelFlag): boolean {
369
+ if (this.userAccessLevel === undefined) {
370
+ return true;
374
371
  }
372
+ return (this.userAccessLevel & accessLevel) === accessLevel;
375
373
  }
376
- return true;
377
- }
378
- private checkPermissionAndAccessLevelPrivate(
379
- context: ISessionContext,
380
- permission: PermissionType,
381
- accessLevel: AccessLevelFlag): boolean {
382
- if (!this.checkPermissionPrivate(context, permission)) {
383
- return false;
384
- }
385
- return this.checkAccessLevelPrivate(context, accessLevel);
386
- }
387
-
388
- public isReadable(context: ISessionContext): boolean {
389
- return (this.accessLevel & AccessLevelFlag.CurrentRead) === AccessLevelFlag.CurrentRead;
390
- }
391
-
392
- public isUserReadable(context: ISessionContext): boolean {
393
- if (!this.isReadable(context)) {
394
- return false;
374
+ private checkPermissionPrivate(
375
+ context: ISessionContext,
376
+ permission: PermissionType,
377
+ ): boolean {
378
+ if (!context) return true;
379
+ assert(context instanceof SessionContext);
380
+ if (context.checkPermission) {
381
+ if (!(context.checkPermission instanceof Function)) {
382
+ errorLog("context checkPermission is not a function");
383
+ return false;
384
+ }
385
+ if (!context.checkPermission(this, permission)) {
386
+ return false;
387
+ }
388
+ }
389
+ return true;
395
390
  }
396
- if (!this.checkPermissionPrivate(context, PermissionType.Read)) {
397
- return false;
391
+ private checkPermissionAndAccessLevelPrivate(
392
+ context: ISessionContext,
393
+ permission: PermissionType,
394
+ accessLevel: AccessLevelFlag): boolean {
395
+ if (!this.checkPermissionPrivate(context, permission)) {
396
+ return false;
397
+ }
398
+ return this.checkAccessLevelPrivate(context, accessLevel);
398
399
  }
399
- return this.checkAccessLevelPrivate(context, AccessLevelFlag.CurrentRead);
400
- }
401
-
402
- public isWritable(context: ISessionContext): boolean {
403
- return (this.accessLevel & AccessLevelFlag.CurrentWrite) === AccessLevelFlag.CurrentWrite;
404
- }
405
400
 
406
- public isUserWritable(context: ISessionContext): boolean {
407
- if (!this.isWritable(context)) {
408
- return false;
401
+ public isReadable(context: ISessionContext): boolean {
402
+ return (this.accessLevel & AccessLevelFlag.CurrentRead) === AccessLevelFlag.CurrentRead;
409
403
  }
410
- return this.checkPermissionAndAccessLevelPrivate(context, PermissionType.Write, AccessLevelFlag.CurrentWrite);
411
- }
412
404
 
413
- public canUserReadHistory(context: ISessionContext): boolean {
414
- return this.checkPermissionAndAccessLevelPrivate(context, PermissionType.ReadHistory, AccessLevelFlag.HistoryRead);
415
- }
416
- public canUserWriteHistorizingAttribute(context: ISessionContext): boolean {
417
- if (context && !context.checkPermission(this, PermissionType.WriteHistorizing)) {
418
- return false;
405
+ public isUserReadable(context: ISessionContext): boolean {
406
+ if (!this.isReadable(context)) {
407
+ return false;
408
+ }
409
+ if (!this.checkPermissionPrivate(context, PermissionType.Read)) {
410
+ return false;
411
+ }
412
+ return this.checkAccessLevelPrivate(context, AccessLevelFlag.CurrentRead);
419
413
  }
420
- return true;
421
- }
422
- public canUserInsertHistory(context: ISessionContext): boolean {
423
- return this.checkPermissionAndAccessLevelPrivate(context, PermissionType.InsertHistory, AccessLevelFlag.HistoryWrite);
424
- }
425
- public canUserModifyHistory(context: ISessionContext): boolean {
426
- return this.checkPermissionAndAccessLevelPrivate(context, PermissionType.ModifyHistory, AccessLevelFlag.HistoryWrite);
427
- }
428
- public canUserDeleteHistory(context: ISessionContext): boolean {
429
- return this.checkPermissionAndAccessLevelPrivate(context, PermissionType.DeleteHistory, AccessLevelFlag.HistoryWrite);
430
- }
431
414
 
432
- /**
433
- *
434
- *
435
- * from OPC.UA.Spec 1.02 part 4
436
- * 5.10.2.4 StatusCodes
437
- * Table 51 defines values for the operation level statusCode contained in the DataValue structure of
438
- * each values element. Common StatusCodes are defined in Table 166.
439
- *
440
- * Table 51 Read Operation Level Result Codes
441
- *
442
- * Symbolic Id Description
443
- *
444
- * BadNodeIdInvalid The syntax of the node id is not valid.
445
- * BadNodeIdUnknown The node id refers to a node that does not exist in the server address space.
446
- * BadAttributeIdInvalid BadAttributeIdInvalid The attribute is not supported for the specified node.
447
- * BadIndexRangeInvalid The syntax of the index range parameter is invalid.
448
- * BadIndexRangeNoData No data exists within the range of indexes specified.
449
- * BadDataEncodingInvalid The data encoding is invalid.
450
- * This result is used if no dataEncoding can be applied because an Attribute other
451
- * than Value was requested or the DataType of the Value Attribute is not a subtype
452
- * of the Structure DataType.
453
- * BadDataEncodingUnsupported The server does not support the requested data encoding for the node.
454
- * This result is used if a dataEncoding can be applied but the passed data encoding
455
- * is not known to the Server.
456
- * BadNotReadable The access level does not allow reading or subscribing to the Node.
457
- * BadUserAccessDenied User does not have permission to perform the requested operation. (table 165)
458
- */
459
- public readValue(
460
- context?: ISessionContext | null,
461
- indexRange?: NumericRange,
462
- dataEncoding?: QualifiedNameLike | null
463
- ): DataValue {
464
- if (!context) {
465
- context = SessionContext.defaultContext;
415
+ public isWritable(context: ISessionContext): boolean {
416
+ return (this.accessLevel & AccessLevelFlag.CurrentWrite) === AccessLevelFlag.CurrentWrite;
466
417
  }
467
418
 
468
- if (context.isAccessRestricted(this)) {
469
- return new DataValue({ statusCode: StatusCodes.BadSecurityModeInsufficient });
419
+ public isUserWritable(context: ISessionContext): boolean {
420
+ if (!this.isWritable(context)) {
421
+ return false;
422
+ }
423
+ return this.checkPermissionAndAccessLevelPrivate(context, PermissionType.Write, AccessLevelFlag.CurrentWrite);
470
424
  }
471
425
 
472
- if (!this.isReadable(context)) {
473
- return new DataValue({ statusCode: StatusCodes.BadNotReadable });
474
- }
475
- if (!this.checkPermissionPrivate(context, PermissionType.Read)) {
476
- return new DataValue({ statusCode: StatusCodes.BadUserAccessDenied });
426
+ public canUserReadHistory(context: ISessionContext): boolean {
427
+ return this.checkPermissionAndAccessLevelPrivate(context, PermissionType.ReadHistory, AccessLevelFlag.HistoryRead);
477
428
  }
478
- if (!this.isUserReadable(context)) {
479
- return new DataValue({ statusCode: StatusCodes.BadNotReadable });
480
- }
481
- if (!isValidDataEncoding(dataEncoding)) {
482
- return new DataValue({ statusCode: StatusCodes.BadDataEncodingInvalid });
429
+ public canUserWriteHistorizingAttribute(context: ISessionContext): boolean {
430
+ if (context && !context.checkPermission(this, PermissionType.WriteHistorizing)) {
431
+ return false;
432
+ }
433
+ return true;
483
434
  }
435
+ public canUserInsertHistory(context: ISessionContext): boolean {
436
+ return this.checkPermissionAndAccessLevelPrivate(context, PermissionType.InsertHistory, AccessLevelFlag.HistoryWrite);
437
+ }
438
+ public canUserModifyHistory(context: ISessionContext): boolean {
439
+ return this.checkPermissionAndAccessLevelPrivate(context, PermissionType.ModifyHistory, AccessLevelFlag.HistoryWrite);
440
+ }
441
+ public canUserDeleteHistory(context: ISessionContext): boolean {
442
+ return this.checkPermissionAndAccessLevelPrivate(context, PermissionType.DeleteHistory, AccessLevelFlag.HistoryWrite);
443
+ }
444
+
445
+ /**
446
+ *
447
+ *
448
+ * from OPC.UA.Spec 1.02 part 4
449
+ * 5.10.2.4 StatusCodes
450
+ * Table 51 defines values for the operation level statusCode contained in the DataValue structure of
451
+ * each values element. Common StatusCodes are defined in Table 166.
452
+ *
453
+ * Table 51 Read Operation Level Result Codes
454
+ *
455
+ * Symbolic Id Description
456
+ *
457
+ * BadNodeIdInvalid The syntax of the node id is not valid.
458
+ * BadNodeIdUnknown The node id refers to a node that does not exist in the server address space.
459
+ * BadAttributeIdInvalid BadAttributeIdInvalid The attribute is not supported for the specified node.
460
+ * BadIndexRangeInvalid The syntax of the index range parameter is invalid.
461
+ * BadIndexRangeNoData No data exists within the range of indexes specified.
462
+ * BadDataEncodingInvalid The data encoding is invalid.
463
+ * This result is used if no dataEncoding can be applied because an Attribute other
464
+ * than Value was requested or the DataType of the Value Attribute is not a subtype
465
+ * of the Structure DataType.
466
+ * BadDataEncodingUnsupported The server does not support the requested data encoding for the node.
467
+ * This result is used if a dataEncoding can be applied but the passed data encoding
468
+ * is not known to the Server.
469
+ * BadNotReadable The access level does not allow reading or subscribing to the Node.
470
+ * BadUserAccessDenied User does not have permission to perform the requested operation. (table 165)
471
+ */
472
+ public readValue(
473
+ context?: ISessionContext | null,
474
+ indexRange?: NumericRange,
475
+ dataEncoding?: QualifiedNameLike | null
476
+ ): DataValue {
477
+ if (!context) {
478
+ context = SessionContext.defaultContext;
479
+ }
480
+
481
+ if (context.isAccessRestricted(this)) {
482
+ return new DataValue({ statusCode: StatusCodes.BadSecurityModeInsufficient });
483
+ }
484
+
485
+ if (!this.isReadable(context)) {
486
+ return new DataValue({ statusCode: StatusCodes.BadNotReadable });
487
+ }
488
+ if (!this.checkPermissionPrivate(context, PermissionType.Read)) {
489
+ return new DataValue({ statusCode: StatusCodes.BadUserAccessDenied });
490
+ }
491
+ if (!this.isUserReadable(context)) {
492
+ return new DataValue({ statusCode: StatusCodes.BadNotReadable });
493
+ }
494
+ if (!isValidDataEncoding(dataEncoding)) {
495
+ return new DataValue({ statusCode: StatusCodes.BadDataEncodingInvalid });
496
+ }
484
497
 
485
- if (this._timestamped_get_func) {
486
- if (this._timestamped_get_func.length === 0) {
487
- const dataValueOrPromise = (this._timestamped_get_func as VariableDataValueGetterSync)();
488
- if (!Object.prototype.hasOwnProperty.call(dataValueOrPromise, "then")) {
489
- if (dataValueOrPromise !== this.$dataValue) {
490
- // TO DO : is this necessary ? this may interfere with current use of $dataValue
491
- this.$dataValue = dataValueOrPromise as DataValue;
492
- this.verifyVariantCompatibility(this.$dataValue.value);
498
+ if (this._timestamped_get_func) {
499
+ if (this._timestamped_get_func.length === 0) {
500
+ const dataValueOrPromise = (this._timestamped_get_func as VariableDataValueGetterSync)();
501
+ if (!Object.prototype.hasOwnProperty.call(dataValueOrPromise, "then")) {
502
+ if (dataValueOrPromise !== this.$dataValue) {
503
+ // TO DO : is this necessary ? this may interfere with current use of $dataValue
504
+ this.$dataValue = dataValueOrPromise as DataValue;
505
+ this.verifyVariantCompatibility(this.$dataValue.value);
506
+ }
507
+ } else {
508
+ errorLog("Unsupported: _timestamped_get_func returns a Promise !");
493
509
  }
494
- } else {
495
- errorLog("Unsupported: _timestamped_get_func returns a Promise !");
496
510
  }
497
511
  }
498
- }
499
512
 
500
- let dataValue = this.$dataValue;
513
+ let dataValue = this.$dataValue;
501
514
 
502
- if (dataValue.statusCode.isGoodish()) {
503
- // note : extractRange will clone the dataValue
504
- dataValue = extractRange(dataValue, indexRange);
505
- }
515
+ if (dataValue.statusCode.isGoodish()) {
516
+ // note : extractRange will clone the dataValue
517
+ dataValue = extractRange(dataValue, indexRange);
518
+ }
506
519
 
507
- /* istanbul ignore next */
508
- if (
509
- dataValue.statusCode.equals(StatusCodes.BadWaitingForInitialData) ||
510
- dataValue.statusCode.equals(StatusCodes.UncertainInitialValue)
511
- ) {
512
- debugLog(
513
- chalk.red(" Warning: UAVariable#readValue ") +
514
- chalk.cyan(this.browseName.toString()) +
515
- " (" +
516
- chalk.yellow(this.nodeId.toString()) +
517
- ") exists but dataValue has not been defined"
518
- );
520
+ /* istanbul ignore next */
521
+ if (
522
+ dataValue.statusCode.equals(StatusCodes.BadWaitingForInitialData) ||
523
+ dataValue.statusCode.equals(StatusCodes.UncertainInitialValue)
524
+ ) {
525
+ debugLog(
526
+ chalk.red(" Warning: UAVariable#readValue ") +
527
+ chalk.cyan(this.browseName.toString()) +
528
+ " (" +
529
+ chalk.yellow(this.nodeId.toString()) +
530
+ ") exists but dataValue has not been defined"
531
+ );
532
+ }
533
+ return dataValue;
519
534
  }
520
- return dataValue;
521
- }
522
-
523
- public isEnumeration(): boolean {
524
- return this.addressSpacePrivate.isEnumeration(this.dataType);
525
- }
526
535
 
527
- public isExtensionObject(): boolean {
528
- // DataType must be one of Structure
529
- if (this.dataType.isEmpty()) return false;
530
- const dataTypeNode = this.addressSpace.findDataType(this.dataType) as UADataType;
531
- if (!dataTypeNode) {
532
- throw new Error(" Cannot find DataType " + this.dataType.toString() + " in standard address Space");
536
+ public isEnumeration(): boolean {
537
+ return this.addressSpacePrivate.isEnumeration(this.dataType);
533
538
  }
534
- const structureNode = this.addressSpace.findDataType("Structure")!;
535
- if (!structureNode) {
536
- throw new Error(" Cannot find 'Structure' DataType in standard address Space");
537
- }
538
- return dataTypeNode.isSupertypeOf(structureNode);
539
- }
540
539
 
541
- public _getEnumerationInfo(): EnumerationInfo {
542
- // DataType must be one of Enumeration
543
- assert(this.isEnumeration(), "Variable is not an enumeration");
544
- const dataTypeNode = this.addressSpace.findDataType(this.dataType)! as UADataTypeImpl;
545
- return dataTypeNode._getEnumerationInfo();
546
- }
540
+ public isExtensionObject(): boolean {
541
+ // DataType must be one of Structure
542
+ if (this.dataType.isEmpty()) return false;
543
+ const dataTypeNode = this.addressSpace.findDataType(this.dataType) as UADataType;
544
+ if (!dataTypeNode) {
545
+ throw new Error(" Cannot find DataType " + this.dataType.toString() + " in standard address Space");
546
+ }
547
+ const structureNode = this.addressSpace.findDataType("Structure")!;
548
+ if (!structureNode) {
549
+ throw new Error(" Cannot find 'Structure' DataType in standard address Space");
550
+ }
551
+ return dataTypeNode.isSubtypeOf(structureNode);
552
+ }
547
553
 
548
- public asyncRefresh(oldestDate: Date, callback: CallbackT<DataValue>): void;
549
- public asyncRefresh(oldestDate: Date): Promise<DataValue>;
550
- public asyncRefresh(...args: any[]): any {
551
- if (this.$dataValue.statusCode.isGoodish()) {
552
- this.verifyVariantCompatibility(this.$dataValue.value);
554
+ public _getEnumerationInfo(): EnumerationInfo {
555
+ // DataType must be one of Enumeration
556
+ assert(this.isEnumeration(), "Variable is not an enumeration");
557
+ const dataTypeNode = this.addressSpace.findDataType(this.dataType)! as UADataTypeImpl;
558
+ return dataTypeNode._getEnumerationInfo();
553
559
  }
554
560
 
555
- const oldestDate = args[0] as Date;
556
- assert(oldestDate instanceof Date);
557
- const callback = args[1] as CallbackT<DataValue>;
561
+ public asyncRefresh(oldestDate: Date, callback: CallbackT<DataValue>): void;
562
+ public asyncRefresh(oldestDate: Date): Promise<DataValue>;
563
+ public asyncRefresh(...args: any[]): any {
564
+ if (this.$dataValue.statusCode.isGoodish()) {
565
+ this.verifyVariantCompatibility(this.$dataValue.value);
566
+ }
558
567
 
559
- if (!this.refreshFunc) {
560
- // no refresh func
561
- const dataValue = this.readValue();
562
- dataValue.serverTimestamp = oldestDate;
563
- dataValue.serverPicoseconds = 0;
564
- if (oldestDate.getTime() <= dataValue.serverTimestamp!.getTime()) {
565
- return callback(null, dataValue);
566
- } else {
567
- // fake
568
+ const oldestDate = args[0] as Date;
569
+ assert(oldestDate instanceof Date);
570
+ const callback = args[1] as CallbackT<DataValue>;
571
+
572
+ if (!this.refreshFunc) {
573
+ // no refresh func
574
+ const dataValue = this.readValue();
575
+ dataValue.serverTimestamp = oldestDate;
576
+ dataValue.serverPicoseconds = 0;
577
+ if (oldestDate.getTime() <= dataValue.serverTimestamp!.getTime()) {
578
+ return callback(null, dataValue);
579
+ } else {
580
+ // fake
581
+ return callback(null, dataValue);
582
+ }
583
+ }
584
+
585
+ if (this.$dataValue.serverTimestamp && oldestDate.getTime() <= this.$dataValue.serverTimestamp!.getTime()) {
586
+ const dataValue = this.readValue();
587
+ dataValue.serverTimestamp = oldestDate;
588
+ dataValue.serverPicoseconds = 0;
568
589
  return callback(null, dataValue);
569
590
  }
591
+ try {
592
+ this.refreshFunc.call(this, (err: Error | null, dataValue?: DataValueLike) => {
593
+ // istanbul ignore next
594
+ if (err || !dataValue) {
595
+ errorLog(
596
+ "-------------- refresh call failed",
597
+ this.browseName.toString(),
598
+ this.nodeId.toString(),
599
+ err?.message
600
+ );
601
+ dataValue = { statusCode: StatusCodes.BadNoDataAvailable };
602
+ }
603
+ if (dataValue !== this.$dataValue) {
604
+ this._internal_set_dataValue(coerceDataValue(dataValue), null);
605
+ }
606
+ callback(err, this.$dataValue);
607
+ });
608
+ } catch (err) {
609
+ errorLog("-------------- refresh call failed 2", this.browseName.toString(), this.nodeId.toString());
610
+ errorLog(err);
611
+ const dataValue = new DataValue({ statusCode: StatusCodes.BadInternalError });
612
+ this._internal_set_dataValue(dataValue, null);
613
+ callback(err as Error, this.$dataValue);
614
+ }
570
615
  }
571
616
 
572
- if (this.$dataValue.serverTimestamp && oldestDate.getTime() <= this.$dataValue.serverTimestamp!.getTime()) {
573
- const dataValue = this.readValue();
574
- dataValue.serverTimestamp = oldestDate;
575
- dataValue.serverPicoseconds = 0;
576
- return callback(null, dataValue);
577
- }
578
- try {
579
- this.refreshFunc.call(this, (err: Error | null, dataValue?: DataValueLike) => {
580
- // istanbul ignore next
581
- if (err || !dataValue) {
582
- errorLog(
583
- "-------------- refresh call failed",
584
- this.browseName.toString(),
585
- this.nodeId.toString(),
586
- err?.message
587
- );
588
- dataValue = { statusCode: StatusCodes.BadNoDataAvailable };
589
- }
590
- if (dataValue !== this.$dataValue) {
591
- this._internal_set_dataValue(coerceDataValue(dataValue), null);
592
- }
593
- callback(err, this.$dataValue);
594
- });
595
- } catch (err) {
596
- errorLog("-------------- refresh call failed 2", this.browseName.toString(), this.nodeId.toString());
597
- errorLog(err);
598
- const dataValue = new DataValue({ statusCode: StatusCodes.BadInternalError });
599
- this._internal_set_dataValue(dataValue, null);
600
- callback(err as Error, this.$dataValue);
617
+ public readEnumValue(): IEnumItem {
618
+ const value = this.readValue().value.value as number;
619
+ const enumInfo = this._getEnumerationInfo();
620
+ const enumV = enumInfo.valueIndex[value];
621
+ return { value, name: enumV ? enumV.name : "?????" };
601
622
  }
602
- }
603
-
604
- public readEnumValue(): IEnumItem {
605
- const value = this.readValue().value.value as number;
606
- const enumInfo = this._getEnumerationInfo();
607
- const enumV = enumInfo.valueIndex[value];
608
- return { value, name: enumV ? enumV.name : "?????" };
609
- }
610
623
 
611
- public writeEnumValue(value: string | number): void {
612
- const enumInfo = this._getEnumerationInfo();
624
+ public writeEnumValue(value: string | number): void {
625
+ const enumInfo = this._getEnumerationInfo();
613
626
 
614
- if (typeof value === "string") {
615
- if (!Object.prototype.hasOwnProperty.call(enumInfo.nameIndex, value)) {
616
- const possibleValues = Object.keys(enumInfo.nameIndex).join(",");
617
- throw new Error("UAVariable#writeEnumValue: cannot find value " + value + " in [" + possibleValues + "]");
627
+ if (typeof value === "string") {
628
+ if (!Object.prototype.hasOwnProperty.call(enumInfo.nameIndex, value)) {
629
+ const possibleValues = Object.keys(enumInfo.nameIndex).join(",");
630
+ throw new Error("UAVariable#writeEnumValue: cannot find value " + value + " in [" + possibleValues + "]");
631
+ }
632
+ const valueIndex = enumInfo.nameIndex[value].value;
633
+ value = valueIndex;
618
634
  }
619
- const valueIndex = enumInfo.nameIndex[value].value;
620
- value = valueIndex;
621
- }
622
- if (isFinite(value)) {
623
- const possibleValues = Object.keys(enumInfo.nameIndex).join(",");
635
+ if (isFinite(value)) {
636
+ const possibleValues = Object.keys(enumInfo.nameIndex).join(",");
624
637
 
625
- if (!enumInfo.valueIndex[value]) {
626
- throw new Error("UAVariable#writeEnumValue : value out of range " + value + " in [" + possibleValues + "]");
638
+ if (!enumInfo.valueIndex[value]) {
639
+ throw new Error("UAVariable#writeEnumValue : value out of range " + value + " in [" + possibleValues + "]");
640
+ }
641
+ this.setValueFromSource({
642
+ dataType: DataType.Int32,
643
+ value
644
+ });
645
+ } else {
646
+ throw new Error("UAVariable#writeEnumValue: value type mismatch");
627
647
  }
628
- this.setValueFromSource({
629
- dataType: DataType.Int32,
630
- value
631
- });
632
- } else {
633
- throw new Error("UAVariable#writeEnumValue: value type mismatch");
634
648
  }
635
- }
636
649
 
637
- public readAttribute(
638
- context: ISessionContext | null,
639
- attributeId: AttributeIds,
640
- indexRange?: NumericRange,
641
- dataEncoding?: QualifiedNameLike | null
642
- ): DataValue {
643
- context = context || SessionContext.defaultContext;
644
- assert(context instanceof SessionContext);
650
+ public readAttribute(
651
+ context: ISessionContext | null,
652
+ attributeId: AttributeIds,
653
+ indexRange?: NumericRange,
654
+ dataEncoding?: QualifiedNameLike | null
655
+ ): DataValue {
656
+ context = context || SessionContext.defaultContext;
657
+ assert(context instanceof SessionContext);
645
658
 
646
- const options: DataValueLike = {};
659
+ const options: DataValueLike = {};
647
660
 
648
- if (attributeId !== AttributeIds.Value) {
649
- if (indexRange && indexRange.isDefined()) {
650
- options.statusCode = StatusCodes.BadIndexRangeNoData;
651
- return new DataValue(options);
652
- }
653
- if (isDataEncoding(dataEncoding)) {
654
- options.statusCode = StatusCodes.BadDataEncodingInvalid;
655
- return new DataValue(options);
661
+ if (attributeId !== AttributeIds.Value) {
662
+ if (indexRange && indexRange.isDefined()) {
663
+ options.statusCode = StatusCodes.BadIndexRangeNoData;
664
+ return new DataValue(options);
665
+ }
666
+ if (isDataEncoding(dataEncoding)) {
667
+ options.statusCode = StatusCodes.BadDataEncodingInvalid;
668
+ return new DataValue(options);
669
+ }
656
670
  }
657
- }
658
671
 
659
- switch (attributeId) {
660
- case AttributeIds.Value:
661
- return this.readValue(context, indexRange, dataEncoding);
672
+ switch (attributeId) {
673
+ case AttributeIds.Value:
674
+ return this.readValue(context, indexRange, dataEncoding);
662
675
 
663
- case AttributeIds.DataType:
664
- return this._readDataType();
676
+ case AttributeIds.DataType:
677
+ return this._readDataType();
665
678
 
666
- case AttributeIds.ValueRank:
667
- return this._readValueRank();
679
+ case AttributeIds.ValueRank:
680
+ return this._readValueRank();
668
681
 
669
- case AttributeIds.ArrayDimensions:
670
- return this._readArrayDimensions();
682
+ case AttributeIds.ArrayDimensions:
683
+ return this._readArrayDimensions();
671
684
 
672
- case AttributeIds.AccessLevel:
673
- return this._readAccessLevel(context);
685
+ case AttributeIds.AccessLevel:
686
+ return this._readAccessLevel(context);
674
687
 
675
- case AttributeIds.UserAccessLevel:
676
- return this._readUserAccessLevel(context);
688
+ case AttributeIds.UserAccessLevel:
689
+ return this._readUserAccessLevel(context);
677
690
 
678
- case AttributeIds.MinimumSamplingInterval:
679
- return this._readMinimumSamplingInterval();
691
+ case AttributeIds.MinimumSamplingInterval:
692
+ return this._readMinimumSamplingInterval();
680
693
 
681
- case AttributeIds.Historizing:
682
- return this._readHistorizing();
694
+ case AttributeIds.Historizing:
695
+ return this._readHistorizing();
683
696
 
684
- case AttributeIds.AccessLevelEx:
685
- return this._readAccessLevelEx(context);
686
- default:
687
- return BaseNodeImpl.prototype.readAttribute.call(this, context, attributeId);
697
+ case AttributeIds.AccessLevelEx:
698
+ return this._readAccessLevelEx(context);
699
+ default:
700
+ return BaseNodeImpl.prototype.readAttribute.call(this, context, attributeId);
701
+ }
688
702
  }
689
- }
690
703
 
691
- public getBasicDataType(): DataType {
692
- if (this._basicDataType) {
693
- return this._basicDataType;
704
+ public getBasicDataType(): DataType {
705
+ if (this._basicDataType) {
706
+ return this._basicDataType;
707
+ }
708
+ if (this.dataType.namespace === 0 && this.dataType.value === 0) {
709
+ return DataType.Null;
710
+ }
711
+ const addressSpace = this.addressSpace;
712
+ if (!addressSpace) {
713
+ // may be node has been deleted already
714
+ return DataType.Null;
715
+ }
716
+ const dataTypeNode = addressSpace.findDataType(this.dataType)!;
717
+ const basicDataType =
718
+ dataTypeNode && dataTypeNode.nodeClass === NodeClass.DataType ? dataTypeNode.getBasicDataType() : DataType.Null;
719
+ // const basicDataType = addressSpace.findCorrespondingBasicDataType(this.dataType);
720
+ this._basicDataType = basicDataType;
721
+ return basicDataType;
694
722
  }
695
- if (this.dataType.namespace === 0 && this.dataType.value === 0) {
696
- return DataType.Null;
723
+ public adjustVariant(variant: Variant): Variant {
724
+ return adjustVariant(variant, this.valueRank, this.getBasicDataType());
697
725
  }
698
- const addressSpace = this.addressSpace;
699
- if (!addressSpace) {
700
- // may be node has been deleted already
701
- return DataType.Null;
702
- }
703
- const dataTypeNode = addressSpace.findDataType(this.dataType)!;
704
- const basicDataType =
705
- dataTypeNode && dataTypeNode.nodeClass === NodeClass.DataType ? dataTypeNode.getBasicDataType() : DataType.Null;
706
- // const basicDataType = addressSpace.findCorrespondingBasicDataType(this.dataType);
707
- this._basicDataType = basicDataType;
708
- return basicDataType;
709
- }
710
- public adjustVariant(variant: Variant): Variant {
711
- return adjustVariant(variant, this.valueRank, this.getBasicDataType());
712
- }
713
- public verifyVariantCompatibility(variant: Variant): void {
714
- try {
715
- // istanbul ignore next
716
- if (Object.prototype.hasOwnProperty.call(variant, "value")) {
717
- if (variant.dataType === null || variant.dataType === undefined) {
718
- throw new Error(
719
- "Variant must provide a valid dataType : variant = " +
720
- variant.toString() +
721
- " this.dataType= " +
722
- this.dataType.toString()
723
- );
726
+ public verifyVariantCompatibility(variant: Variant): void {
727
+ try {
728
+ // istanbul ignore next
729
+ if (Object.prototype.hasOwnProperty.call(variant, "value")) {
730
+ if (variant.dataType === null || variant.dataType === undefined) {
731
+ throw new Error(
732
+ "Variant must provide a valid dataType : variant = " +
733
+ variant.toString() +
734
+ " this.dataType= " +
735
+ this.dataType.toString()
736
+ );
737
+ }
738
+ if (
739
+ variant.dataType === DataType.Boolean &&
740
+ (this.dataType.namespace !== 0 || this.dataType.value !== DataType.Boolean)
741
+ ) {
742
+ throw new Error(
743
+ "Variant must provide a valid Boolean : variant = " +
744
+ variant.toString() +
745
+ " this.dataType= " +
746
+ this.dataType.toString()
747
+ );
748
+ }
749
+ if (
750
+ this.dataType.namespace === 0 &&
751
+ this.dataType.value === DataType.LocalizedText &&
752
+ variant.dataType !== DataType.LocalizedText &&
753
+ variant.dataType !== DataType.Null
754
+ ) {
755
+ throw new Error(
756
+ "Variant must provide a valid LocalizedText : variant = " +
757
+ variant.toString() +
758
+ " this.dataType= " +
759
+ this.dataType.toString()
760
+ );
761
+ }
724
762
  }
725
- if (
726
- variant.dataType === DataType.Boolean &&
727
- (this.dataType.namespace !== 0 || this.dataType.value !== DataType.Boolean)
728
- ) {
729
- throw new Error(
730
- "Variant must provide a valid Boolean : variant = " +
731
- variant.toString() +
732
- " this.dataType= " +
733
- this.dataType.toString()
734
- );
763
+ const basicType = this.getBasicDataType();
764
+
765
+ if (basicType === DataType.String && variant.dataType === DataType.ByteString) {
766
+ return; // this is allowed
767
+ }
768
+ if (basicType === DataType.ByteString && variant.dataType === DataType.String) {
769
+ return; // this is allowed
735
770
  }
771
+
736
772
  if (
737
- this.dataType.namespace === 0 &&
738
- this.dataType.value === DataType.LocalizedText &&
739
- variant.dataType !== DataType.LocalizedText &&
740
- variant.dataType !== DataType.Null
773
+ basicType !== DataType.Null &&
774
+ basicType !== DataType.Variant &&
775
+ variant.dataType !== DataType.Null &&
776
+ variant.dataType !== basicType
741
777
  ) {
742
- throw new Error(
743
- "Variant must provide a valid LocalizedText : variant = " +
744
- variant.toString() +
745
- " this.dataType= " +
746
- this.dataType.toString()
747
- );
778
+ const message =
779
+ "UAVariable.setValueFromSource " +
780
+ this.browseName.toString() +
781
+ " nodeId:" +
782
+ this.nodeId.toString() +
783
+ " dataType:" +
784
+ this.dataType.toString() +
785
+ ":\n" +
786
+ "the provided variant must have the expected dataType!\n" +
787
+ " - the expected dataType is " +
788
+ chalk.cyan(DataType[basicType]) +
789
+ "\n" +
790
+ " - the actual dataType is " +
791
+ chalk.magenta(DataType[variant.dataType]) +
792
+ "\n" +
793
+ " - " +
794
+ variant.toString();
795
+ throw new Error(message);
748
796
  }
797
+ } catch (err) {
798
+ errorLog("UAVariable ", (err as Error)?.message, this.browseName.toString(), " nodeId=", this.nodeId.toString());
799
+ errorLog((err as Error).message);
800
+ errorLog((err as Error).stack);
801
+ throw err;
749
802
  }
750
- const basicType = this.getBasicDataType();
751
-
752
- if (basicType === DataType.String && variant.dataType === DataType.ByteString) {
753
- return; // this is allowed
754
- }
755
- if (basicType === DataType.ByteString && variant.dataType === DataType.String) {
756
- return; // this is allowed
803
+ }
804
+ /**
805
+ * setValueFromSource is used to let the device sets the variable values
806
+ * this method also records the current time as sourceTimestamp and serverTimestamp.
807
+ . *
808
+ * The method will raise an exception if the value is not compatible with the dataType and expected dimension
809
+ *
810
+ * @method setValueFromSource
811
+ * @param variant {Variant}
812
+ * @param [statusCode {StatusCode} = StatusCodes.Good]
813
+ * @param [sourceTimestamp= Now]
814
+ */
815
+ public setValueFromSource(variant: VariantLike, statusCode?: StatusCode, sourceTimestamp?: Date): void {
816
+ try {
817
+ statusCode = statusCode || StatusCodes.Good;
818
+ const variant1 = Variant.coerce(variant);
819
+ this.verifyVariantCompatibility(variant1);
820
+ const now = coerceClock(sourceTimestamp, 0);
821
+
822
+ const dataValue = new DataValue(null);
823
+ dataValue.serverPicoseconds = now.picoseconds;
824
+ dataValue.serverTimestamp = now.timestamp;
825
+ dataValue.sourcePicoseconds = now.picoseconds;
826
+ dataValue.sourceTimestamp = now.timestamp;
827
+ dataValue.statusCode = statusCode;
828
+ dataValue.value = variant1;
829
+
830
+ if (dataValue.value.dataType === DataType.ExtensionObject) {
831
+ const valueIsCorrect = this.checkExtensionObjectIsCorrect(dataValue.value.value);
832
+ if (!valueIsCorrect) {
833
+ errorLog("setValueFromSource Invalid value !");
834
+ errorLog(this.toString());
835
+ errorLog(dataValue.toString());
836
+ this.checkExtensionObjectIsCorrect(dataValue.value.value);
837
+ }
838
+ // ----------------------------------
839
+ if (this.$extensionObject || this.$$extensionObjectArray) {
840
+ // we have an extension object already bound to this node
841
+ // the client is asking us to replace the object entierly by a new one
842
+ // const ext = dataValue.value.value;
843
+ this._internal_set_dataValue(dataValue);
844
+ return;
845
+ } else {
846
+ this.$dataValue = dataValue;
847
+ }
848
+ } else {
849
+ this._internal_set_dataValue(dataValue);
850
+ }
851
+ } catch (err) {
852
+ errorLog("UAVariable#setValueFromString Error : ", this.browseName.toString(), this.nodeId.toString());
853
+ errorLog((err as Error).message);
854
+ errorLog(this.parent?.toString());
855
+ throw err;
757
856
  }
758
-
759
- if (
760
- basicType !== DataType.Null &&
761
- basicType !== DataType.Variant &&
762
- variant.dataType !== DataType.Null &&
763
- variant.dataType !== basicType
764
- ) {
765
- const message =
766
- "UAVariable.setValueFromSource " +
767
- this.browseName.toString() +
768
- " nodeId:" +
769
- this.nodeId.toString() +
770
- " dataType:" +
771
- this.dataType.toString() +
772
- ":\n" +
773
- "the provided variant must have the expected dataType!\n" +
774
- " - the expected dataType is " +
775
- chalk.cyan(DataType[basicType]) +
776
- "\n" +
777
- " - the actual dataType is " +
778
- chalk.magenta(DataType[variant.dataType]) +
779
- "\n" +
780
- " - " +
781
- variant.toString();
782
- throw new Error(message);
783
- }
784
- } catch (err) {
785
- errorLog("UAVariable ", (err as Error)?.message, this.browseName.toString(), " nodeId=", this.nodeId.toString());
786
- errorLog((err as Error).message);
787
- errorLog((err as Error).stack);
788
- throw err;
789
857
  }
790
- }
791
- /**
792
- * setValueFromSource is used to let the device sets the variable values
793
- * this method also records the current time as sourceTimestamp and serverTimestamp.
794
- . *
795
- * The method will raise an exception if the value is not compatible with the dataType and expected dimension
796
- *
797
- * @method setValueFromSource
798
- * @param variant {Variant}
799
- * @param [statusCode {StatusCode} = StatusCodes.Good]
800
- * @param [sourceTimestamp= Now]
801
- */
802
- public setValueFromSource(variant: VariantLike, statusCode?: StatusCode, sourceTimestamp?: Date): void {
803
- try {
804
- statusCode = statusCode || StatusCodes.Good;
805
- const variant1 = Variant.coerce(variant);
806
- this.verifyVariantCompatibility(variant1);
807
- const now = coerceClock(sourceTimestamp, 0);
808
-
809
- const dataValue = new DataValue(null);
810
- dataValue.serverPicoseconds = now.picoseconds;
811
- dataValue.serverTimestamp = now.timestamp;
812
- dataValue.sourcePicoseconds = now.picoseconds;
813
- dataValue.sourceTimestamp = now.timestamp;
814
- dataValue.statusCode = statusCode;
815
- dataValue.value = variant1;
816
858
 
817
- if (dataValue.value.dataType === DataType.ExtensionObject) {
818
- const valueIsCorrect = this.checkExtensionObjectIsCorrect(dataValue.value.value);
819
- if (!valueIsCorrect) {
820
- errorLog("Invalid value !");
821
- errorLog(this.toString());
822
- errorLog(dataValue.toString());
823
- this.checkExtensionObjectIsCorrect(dataValue.value.value);
824
- }
825
- // ----------------------------------
826
- if (this.$extensionObject) {
827
- // we have an extension object already bound to this node
828
- // the client is asking us to replace the object entierly by a new one
829
- // const ext = dataValue.value.value;
830
- this._internal_set_dataValue(dataValue);
831
- return;
859
+ public writeValue(
860
+ context: ISessionContext,
861
+ dataValue: DataValue,
862
+ indexRange: string | NumericRange | null,
863
+ callback: StatusCodeCallback
864
+ ): void;
865
+ public writeValue(context: ISessionContext, dataValue: DataValue, callback: StatusCodeCallback): void;
866
+ public writeValue(
867
+ context: ISessionContext,
868
+ dataValue: DataValue,
869
+ indexRange?: string | NumericRange | null
870
+ ): Promise<StatusCode>;
871
+ public writeValue(context: ISessionContext, dataValue: DataValue, ...args: any[]): any {
872
+ context = context || SessionContext.defaultContext;
873
+ assert(context instanceof SessionContext);
874
+
875
+ if (!dataValue.sourceTimestamp) {
876
+ // source timestamp was not specified by the caller
877
+ // we will set the timestamp ourself with the current clock
878
+ if (context.currentTime) {
879
+ dataValue.sourceTimestamp = context.currentTime.timestamp;
880
+ dataValue.sourcePicoseconds = context.currentTime.picoseconds;
832
881
  } else {
833
- this.$dataValue = dataValue;
882
+ const { timestamp, picoseconds } = getCurrentClock();
883
+ dataValue.sourceTimestamp = timestamp;
884
+ dataValue.sourcePicoseconds = picoseconds;
834
885
  }
835
- } else {
836
- this._internal_set_dataValue(dataValue);
837
886
  }
838
- } catch (err) {
839
- errorLog("UAVariable#setValueFromString Error : ", this.browseName.toString(), this.nodeId.toString());
840
- errorLog((err as Error).message);
841
- errorLog(this.parent?.toString());
842
- throw err;
843
- }
844
- }
845
887
 
846
- public writeValue(
847
- context: ISessionContext,
848
- dataValue: DataValue,
849
- indexRange: string | NumericRange | null,
850
- callback: StatusCodeCallback
851
- ): void;
852
- public writeValue(context: ISessionContext, dataValue: DataValue, callback: StatusCodeCallback): void;
853
- public writeValue(
854
- context: ISessionContext,
855
- dataValue: DataValue,
856
- indexRange?: string | NumericRange | null
857
- ): Promise<StatusCode>;
858
- public writeValue(context: ISessionContext, dataValue: DataValue, ...args: any[]): any {
859
- context = context || SessionContext.defaultContext;
860
- assert(context instanceof SessionContext);
888
+ if (context.currentTime && !dataValue.serverTimestamp) {
889
+ dataValue.serverTimestamp = context.currentTime.timestamp;
890
+ dataValue.serverPicoseconds = context.currentTime.picoseconds;
891
+ }
861
892
 
862
- if (!dataValue.sourceTimestamp) {
863
- // source timestamp was not specified by the caller
864
- // we will set the timestamp ourself with the current clock
865
- if (context.currentTime) {
866
- dataValue.sourceTimestamp = context.currentTime.timestamp;
867
- dataValue.sourcePicoseconds = context.currentTime.picoseconds;
893
+ // adjust arguments if optional indexRange Parameter is not given
894
+ let indexRange: NumericRange | null = null;
895
+ let callback: StatusCodeCallback;
896
+ if (args.length === 1) {
897
+ indexRange = new NumericRange();
898
+ callback = args[0];
899
+ } else if (args.length === 2) {
900
+ indexRange = args[0];
901
+ callback = args[1];
868
902
  } else {
869
- const { timestamp, picoseconds } = getCurrentClock();
870
- dataValue.sourceTimestamp = timestamp;
871
- dataValue.sourcePicoseconds = picoseconds;
903
+ throw new Error("Invalid Number of args");
872
904
  }
873
- }
874
905
 
875
- if (context.currentTime && !dataValue.serverTimestamp) {
876
- dataValue.serverTimestamp = context.currentTime.timestamp;
877
- dataValue.serverPicoseconds = context.currentTime.picoseconds;
878
- }
906
+ assert(typeof callback === "function");
907
+ assert(dataValue instanceof DataValue);
908
+ // index range could be string
909
+ indexRange = NumericRange.coerce(indexRange);
879
910
 
880
- // adjust arguments if optional indexRange Parameter is not given
881
- let indexRange: NumericRange | null = null;
882
- let callback: StatusCodeCallback;
883
- if (args.length === 1) {
884
- indexRange = new NumericRange();
885
- callback = args[0];
886
- } else if (args.length === 2) {
887
- indexRange = args[0];
888
- callback = args[1];
889
- } else {
890
- throw new Error("Invalid Number of args");
891
- }
911
+ // test write permission
912
+ if (!this.isWritable(context)) {
913
+ return callback!(null, StatusCodes.BadNotWritable);
914
+ }
915
+ if (!this.checkPermissionPrivate(context, PermissionType.Write)) {
916
+ return new DataValue({ statusCode: StatusCodes.BadUserAccessDenied });
917
+ }
892
918
 
893
- assert(typeof callback === "function");
894
- assert(dataValue instanceof DataValue);
895
- // index range could be string
896
- indexRange = NumericRange.coerce(indexRange);
897
919
 
898
- // test write permission
899
- if (!this.isWritable(context)) {
900
- return callback!(null, StatusCodes.BadNotWritable);
901
- }
902
- if (!this.checkPermissionPrivate(context, PermissionType.Write)) {
903
- return new DataValue({ statusCode: StatusCodes.BadUserAccessDenied });
904
- }
905
-
906
-
907
- if (!this.isUserWritable(context)) {
908
- return callback!(null, StatusCodes.BadWriteNotSupported);
909
- }
910
-
911
- // adjust special case
912
- const variant = adjustVariant2.call(this, dataValue.value);
913
-
914
- const statusCode = this.checkVariantCompatibility(variant);
915
- if (statusCode.isNot(StatusCodes.Good)) {
916
- return callback!(null, statusCode);
917
- }
920
+ if (!this.isUserWritable(context)) {
921
+ return callback!(null, StatusCodes.BadWriteNotSupported);
922
+ }
918
923
 
919
- const write_func = this._timestamped_set_func || default_func;
924
+ // adjust special case
925
+ const variant = adjustVariant2.call(this, dataValue.value);
920
926
 
921
- if (!write_func) {
922
- warningLog(" warning " + this.nodeId.toString() + " " + this.browseName.toString() + " has no setter. \n");
923
- warningLog("Please make sure to bind the variable or to pass a valid value: new Variant({}) during construction time");
924
- return callback!(null, StatusCodes.BadNotWritable);
925
- }
926
- assert(write_func);
927
+ const statusCode = this.checkVariantCompatibility(variant);
928
+ if (statusCode.isNot(StatusCodes.Good)) {
929
+ return callback!(null, statusCode);
930
+ }
927
931
 
928
- write_func.call(this, dataValue, (err?: Error | null, statusCode1?: StatusCode) => {
929
- if (!err) {
930
- dataValue && this.verifyVariantCompatibility(dataValue.value);
932
+ const write_func = this._timestamped_set_func || default_func;
931
933
 
932
- if (indexRange && !indexRange.isEmpty()) {
933
- if (!indexRange.isValid()) {
934
- return callback!(null, StatusCodes.BadIndexRangeInvalid);
935
- }
934
+ if (!write_func) {
935
+ warningLog(" warning " + this.nodeId.toString() + " " + this.browseName.toString() + " has no setter. \n");
936
+ warningLog("Please make sure to bind the variable or to pass a valid value: new Variant({}) during construction time");
937
+ return callback!(null, StatusCodes.BadNotWritable);
938
+ }
939
+ assert(write_func);
936
940
 
937
- const newArrayOrMatrix = dataValue.value.value;
941
+ write_func.call(this, dataValue, (err?: Error | null, statusCode1?: StatusCode) => {
942
+ if (!err) {
943
+ dataValue && this.verifyVariantCompatibility(dataValue.value);
938
944
 
939
- if (dataValue.value.arrayType === VariantArrayType.Array) {
940
- if (this.$dataValue.value.arrayType !== VariantArrayType.Array) {
941
- return callback(null, StatusCodes.BadTypeMismatch);
945
+ if (indexRange && !indexRange.isEmpty()) {
946
+ if (!indexRange.isValid()) {
947
+ return callback!(null, StatusCodes.BadIndexRangeInvalid);
942
948
  }
943
- // check that destination data is also an array
944
- assert(check_valid_array(this.$dataValue.value.dataType, this.$dataValue.value.value));
945
- const destArr = this.$dataValue.value.value;
946
- const result = indexRange.set_values(destArr, newArrayOrMatrix);
947
949
 
948
- if (result.statusCode.isNot(StatusCodes.Good)) {
949
- return callback!(null, result.statusCode);
950
- }
951
- dataValue.value.value = result.array;
952
-
953
- // scrap original array so we detect range
954
- this.$dataValue.value.value = null;
955
- } else if (dataValue.value.arrayType === VariantArrayType.Matrix) {
956
- const dimensions = this.$dataValue.value.dimensions;
957
- if (this.$dataValue.value.arrayType !== VariantArrayType.Matrix || !dimensions) {
958
- // not a matrix !
950
+ const newArrayOrMatrix = dataValue.value.value;
951
+
952
+ if (dataValue.value.arrayType === VariantArrayType.Array) {
953
+ if (this.$dataValue.value.arrayType !== VariantArrayType.Array) {
954
+ return callback(null, StatusCodes.BadTypeMismatch);
955
+ }
956
+ // check that destination data is also an array
957
+ assert(check_valid_array(this.$dataValue.value.dataType, this.$dataValue.value.value));
958
+ const destArr = this.$dataValue.value.value;
959
+ const result = indexRange.set_values(destArr, newArrayOrMatrix);
960
+
961
+ if (result.statusCode.isNot(StatusCodes.Good)) {
962
+ return callback!(null, result.statusCode);
963
+ }
964
+ dataValue.value.value = result.array;
965
+
966
+ // scrap original array so we detect range
967
+ this.$dataValue.value.value = null;
968
+ } else if (dataValue.value.arrayType === VariantArrayType.Matrix) {
969
+ const dimensions = this.$dataValue.value.dimensions;
970
+ if (this.$dataValue.value.arrayType !== VariantArrayType.Matrix || !dimensions) {
971
+ // not a matrix !
972
+ return callback!(null, StatusCodes.BadTypeMismatch);
973
+ }
974
+ const matrix = this.$dataValue.value.value;
975
+ const result = indexRange.set_values_matrix(
976
+ {
977
+ matrix,
978
+ dimensions
979
+ },
980
+ newArrayOrMatrix
981
+ );
982
+ if (result.statusCode.isNot(StatusCodes.Good)) {
983
+ return callback!(null, result.statusCode);
984
+ }
985
+ dataValue.value.dimensions = this.$dataValue.value.dimensions;
986
+ dataValue.value.value = result.matrix;
987
+
988
+ // scrap original array so we detect range
989
+ this.$dataValue.value.value = null;
990
+ } else {
959
991
  return callback!(null, StatusCodes.BadTypeMismatch);
960
992
  }
961
- const matrix = this.$dataValue.value.value;
962
- const result = indexRange.set_values_matrix(
963
- {
964
- matrix,
965
- dimensions
966
- },
967
- newArrayOrMatrix
968
- );
969
- if (result.statusCode.isNot(StatusCodes.Good)) {
970
- return callback!(null, result.statusCode);
971
- }
972
- dataValue.value.dimensions = this.$dataValue.value.dimensions;
973
- dataValue.value.value = result.matrix;
974
-
975
- // scrap original array so we detect range
976
- this.$dataValue.value.value = null;
977
- } else {
978
- return callback!(null, StatusCodes.BadTypeMismatch);
979
993
  }
980
- }
981
- try {
982
- this._internal_set_dataValue(dataValue, indexRange);
983
- } catch (err) {
984
- if (err instanceof Error) {
985
- warningLog(err.message);
994
+ try {
995
+ this._internal_set_dataValue(dataValue, indexRange);
996
+ } catch (err) {
997
+ if (err instanceof Error) {
998
+ warningLog(err.message);
999
+ }
1000
+ return callback!(null, StatusCodes.BadInternalError);
986
1001
  }
987
- return callback!(null, StatusCodes.BadInternalError);
988
- }
989
- }
990
- callback!(err || null, statusCode1);
991
- });
992
- }
993
-
994
- public writeAttribute(context: ISessionContext | null, writeValue: WriteValueOptions, callback: StatusCodeCallback): void;
995
- public writeAttribute(context: ISessionContext | null, writeValue: WriteValueOptions): Promise<StatusCode>;
996
- public writeAttribute(
997
- context: ISessionContext | null,
998
- writeValueOptions: WriteValueOptions,
999
- callback?: (err: Error | null, statusCode?: StatusCode) => void
1000
- ): any {
1001
- // istanbul ignore next
1002
- if (!callback) {
1003
- throw new Error("Internal error");
1004
- }
1005
-
1006
- if (!this.canUserWriteAttribute(context, writeValueOptions.attributeId!)) {
1007
- return callback(null, StatusCodes.BadUserAccessDenied);
1008
- }
1009
- const writeValue: WriteValue =
1010
- writeValueOptions instanceof WriteValue ? (writeValueOptions as WriteValue) : new WriteValue(writeValueOptions);
1011
-
1012
- context = context || SessionContext.defaultContext;
1013
-
1014
- assert(context instanceof SessionContext);
1015
- assert(writeValue instanceof WriteValue);
1016
- assert(writeValue.value instanceof DataValue);
1017
- assert(writeValue.value!.value instanceof Variant);
1018
- assert(typeof callback === "function");
1019
-
1020
- // Spec 1.0.2 Part 4 page 58
1021
- // If the SourceTimestamp or the ServerTimestamp is specified, the Server shall
1022
- // use these values.
1023
-
1024
- // xx _apply_default_timestamps(writeValue.value);
1025
-
1026
- switch (writeValue.attributeId) {
1027
- case AttributeIds.Value:
1028
- this.writeValue(context, writeValue.value!, writeValue.indexRange!, callback);
1029
- break;
1030
- case AttributeIds.Historizing:
1031
- if (writeValue.value!.value.dataType !== DataType.Boolean) {
1032
- return callback(null, StatusCodes.BadTypeMismatch);
1033
1002
  }
1034
- if (!this.checkPermissionPrivate(context, PermissionType.WriteHistorizing)) {
1035
- return callback(null, StatusCodes.BadUserAccessDenied);
1036
- }
1037
-
1038
- if (!this.canUserWriteHistorizingAttribute(context)) {
1039
- return callback(null, StatusCodes.BadHistoryOperationUnsupported);
1040
- }
1041
-
1042
- // if the variable has no historizing in place reject
1043
- if (!this.getChildByName("HA Configuration")) {
1044
- return callback(null, StatusCodes.BadNotSupported);
1045
- }
1046
- // check if user is allowed to do that !
1047
- // TODO
1048
- this.historizing = !!writeValue.value!.value.value; // yes ! indeed !
1049
- return callback(null, StatusCodes.Good);
1050
-
1051
- default:
1052
- super.writeAttribute(context, writeValue, callback);
1053
- break;
1054
- }
1055
- }
1056
-
1057
- /**
1058
- * @method checkVariantCompatibility
1059
- * note:
1060
- * this method is overridden in address-space-data-access
1061
- * @return {StatusCode}
1062
- */
1063
- public checkVariantCompatibility(value: Variant): StatusCode {
1064
- // test dataType
1065
- if (!this._validate_DataType(value.dataType)) {
1066
- return StatusCodes.BadTypeMismatch;
1067
- }
1068
- try {
1069
- this.verifyVariantCompatibility(value);
1070
- } catch (err) {
1071
- return StatusCodes.BadTypeMismatch;
1003
+ callback!(err || null, statusCode1);
1004
+ });
1072
1005
  }
1073
- return StatusCodes.Good;
1074
- }
1075
1006
 
1076
- /**
1077
- * touch the source timestamp of a Variable and cascade up the change
1078
- * to the parent variable if any.
1079
- */
1080
- public touchValue(optionalNow?: PreciseClock): void {
1081
- const now = optionalNow || getCurrentClock();
1082
- propagateTouchValueUpward(this, now);
1083
- }
1007
+ public writeAttribute(context: ISessionContext | null, writeValue: WriteValueOptions, callback: StatusCodeCallback): void;
1008
+ public writeAttribute(context: ISessionContext | null, writeValue: WriteValueOptions): Promise<StatusCode>;
1009
+ public writeAttribute(
1010
+ context: ISessionContext | null,
1011
+ writeValueOptions: WriteValueOptions,
1012
+ callback?: (err: Error | null, statusCode?: StatusCode) => void
1013
+ ): any {
1014
+ // istanbul ignore next
1015
+ if (!callback) {
1016
+ throw new Error("Internal error");
1017
+ }
1084
1018
 
1085
- /**
1086
- * bind a variable with a get and set functions.
1087
- *
1088
- * properties:
1089
- * - value: a Variant or a status code
1090
- * - sourceTimestamp
1091
- * - sourcePicoseconds
1092
- * @param [options.timestamped_set]
1093
- * @param [options.refreshFunc] the variable asynchronous getter function.
1094
- * @param [overwrite {Boolean} = false] set overwrite to true to overwrite existing binding
1095
- * @return void
1096
- *
1097
- *
1098
- * ### Providing read access to the underlying value
1099
- *
1100
- * #### Variation 1
1101
- *
1102
- * In this variation, the user provides a function that returns a Variant with the current value.
1103
- *
1104
- * The sourceTimestamp will be set automatically.
1105
- *
1106
- * The get function is called synchronously.
1107
- *
1108
- * @example
1109
- *
1110
- *
1111
- * ```javascript
1112
- * ...
1113
- * var options = {
1114
- * get : () => {
1115
- * return new Variant({...});
1116
- * },
1117
- * set : function(variant) {
1118
- * // store the variant somewhere
1119
- * return StatusCodes.Good;
1120
- * }
1121
- * };
1122
- * ...
1123
- * engine.bindVariable(nodeId,options):
1124
- * ...
1125
- * ```
1126
- *
1127
- *
1128
- * #### Variation 2:
1129
- *
1130
- * This variation can be used when the user wants to specify a specific '''sourceTimestamp''' associated
1131
- * with the current value of the UAVariable.
1132
- *
1133
- * The provided ```timestamped_get``` function should return an object with three properties:
1134
- * * value: containing the variant value or a error StatusCode,
1135
- * * sourceTimestamp
1136
- * * sourcePicoseconds
1137
- *
1138
- * ```javascript
1139
- * ...
1140
- * var myDataValue = new DataValue({
1141
- * value: {dataType: DataType.Double , value: 10.0},
1142
- * sourceTimestamp : new Date(),
1143
- * sourcePicoseconds: 0
1144
- * });
1145
- * ...
1146
- * var options = {
1147
- * timestamped_get : () => { return myDataValue; }
1148
- * };
1149
- * ...
1150
- * engine.bindVariable(nodeId,options):
1151
- * ...
1152
- * // record a new value
1153
- * myDataValue.value.value = 5.0;
1154
- * myDataValue.sourceTimestamp = new Date();
1155
- * ...
1156
- * ```
1157
- *
1158
- *
1159
- * #### Variation 3:
1160
- *
1161
- * This variation can be used when the value associated with the variables requires a asynchronous function call to be
1162
- * extracted. In this case, the user should provide an async method ```refreshFunc```.
1163
- *
1164
- *
1165
- * The ```refreshFunc``` shall do whatever is necessary to fetch the most up to date version of the variable value, and
1166
- * call the ```callback``` function when the data is ready.
1167
- *
1168
- *
1169
- * The ```callback``` function follow the standard callback function signature:
1170
- * * the first argument shall be **null** or **Error**, depending of the outcome of the fetch operation,
1171
- * * the second argument shall be a DataValue with the new UAVariable Value, a StatusCode, and time stamps.
1172
- *
1173
- *
1174
- * Optionally, it is possible to pass a sourceTimestamp and a sourcePicoseconds value as a third and fourth arguments
1175
- * of the callback. When sourceTimestamp and sourcePicoseconds are missing, the system will set their default value
1176
- * to the current time..
1177
- *
1178
- *
1179
- * ```javascript
1180
- * ...
1181
- * var options = {
1182
- * refreshFunc : function(callback) {
1183
- * ... do_some_async_stuff_to_get_the_new_variable_value
1184
- * var dataValue = new DataValue({
1185
- * value: new Variant({...}),
1186
- * statusCode: StatusCodes.Good,
1187
- * sourceTimestamp: new Date()
1188
- * });
1189
- * callback(null,dataValue);
1190
- * }
1191
- * };
1192
- * ...
1193
- * variable.bindVariable(nodeId,options):
1194
- * ...
1195
- * ```
1196
- *
1197
- * ### Providing write access to the underlying value
1198
- *
1199
- * #### Variation1 - provide a simple synchronous set function
1200
- *
1201
- *
1202
- * #### Notes
1203
- * to do : explain return StatusCodes.GoodCompletesAsynchronously;
1204
- *
1205
- */
1206
- public bindVariable(options?: BindVariableOptions, overwrite?: boolean): void {
1207
- if (overwrite) {
1208
- this._timestamped_set_func = null;
1209
- this._timestamped_get_func = null;
1210
- this._get_func = null;
1211
- this._set_func = null;
1212
- this.refreshFunc = undefined;
1213
- this._historyRead = UAVariableImpl.prototype._historyRead;
1214
- }
1019
+ if (!this.canUserWriteAttribute(context, writeValueOptions.attributeId!)) {
1020
+ return callback(null, StatusCodes.BadUserAccessDenied);
1021
+ }
1022
+ const writeValue: WriteValue =
1023
+ writeValueOptions instanceof WriteValue ? (writeValueOptions as WriteValue) : new WriteValue(writeValueOptions);
1024
+
1025
+ context = context || SessionContext.defaultContext;
1026
+
1027
+ assert(context instanceof SessionContext);
1028
+ assert(writeValue instanceof WriteValue);
1029
+ assert(writeValue.value instanceof DataValue);
1030
+ assert(writeValue.value!.value instanceof Variant);
1031
+ assert(typeof callback === "function");
1032
+
1033
+ // Spec 1.0.2 Part 4 page 58
1034
+ // If the SourceTimestamp or the ServerTimestamp is specified, the Server shall
1035
+ // use these values.
1036
+
1037
+ // xx _apply_default_timestamps(writeValue.value);
1038
+
1039
+ switch (writeValue.attributeId) {
1040
+ case AttributeIds.Value:
1041
+ this.writeValue(context, writeValue.value!, writeValue.indexRange!, callback);
1042
+ break;
1043
+ case AttributeIds.Historizing:
1044
+ if (writeValue.value!.value.dataType !== DataType.Boolean) {
1045
+ return callback(null, StatusCodes.BadTypeMismatch);
1046
+ }
1047
+ if (!this.checkPermissionPrivate(context, PermissionType.WriteHistorizing)) {
1048
+ return callback(null, StatusCodes.BadUserAccessDenied);
1049
+ }
1215
1050
 
1216
- options = options || {};
1051
+ if (!this.canUserWriteHistorizingAttribute(context)) {
1052
+ return callback(null, StatusCodes.BadHistoryOperationUnsupported);
1053
+ }
1217
1054
 
1218
- assert(typeof this._timestamped_set_func !== "function", "UAVariable already bound");
1219
- assert(typeof this._timestamped_get_func !== "function", "UAVariable already bound");
1055
+ // if the variable has no historizing in place reject
1056
+ if (!this.getChildByName("HA Configuration")) {
1057
+ return callback(null, StatusCodes.BadNotSupported);
1058
+ }
1059
+ // check if user is allowed to do that !
1060
+ // TODO
1061
+ this.historizing = !!writeValue.value!.value.value; // yes ! indeed !
1062
+ return callback(null, StatusCodes.Good);
1063
+
1064
+ default:
1065
+ super.writeAttribute(context, writeValue, callback);
1066
+ break;
1067
+ }
1068
+ }
1220
1069
 
1221
- bind_getter.call(this, options);
1222
- bind_setter.call(this, options);
1070
+ /**
1071
+ * @method checkVariantCompatibility
1072
+ * note:
1073
+ * this method is overridden in address-space-data-access
1074
+ * @return {StatusCode}
1075
+ */
1076
+ public checkVariantCompatibility(value: Variant): StatusCode {
1077
+ // test dataType
1078
+ if (!this._validate_DataType(value.dataType)) {
1079
+ return StatusCodes.BadTypeMismatch;
1080
+ }
1081
+ try {
1082
+ this.verifyVariantCompatibility(value);
1083
+ } catch (err) {
1084
+ return StatusCodes.BadTypeMismatch;
1085
+ }
1086
+ return StatusCodes.Good;
1087
+ }
1088
+
1089
+ /**
1090
+ * touch the source timestamp of a Variable and cascade up the change
1091
+ * to the parent variable if any.
1092
+ */
1093
+ public touchValue(optionalNow?: PreciseClock): void {
1094
+ const now = optionalNow || getCurrentClock();
1095
+ propagateTouchValueUpward(this, now);
1096
+ }
1097
+
1098
+ /**
1099
+ * bind a variable with a get and set functions.
1100
+ *
1101
+ * properties:
1102
+ * - value: a Variant or a status code
1103
+ * - sourceTimestamp
1104
+ * - sourcePicoseconds
1105
+ * @param [options.timestamped_set]
1106
+ * @param [options.refreshFunc] the variable asynchronous getter function.
1107
+ * @param [overwrite {Boolean} = false] set overwrite to true to overwrite existing binding
1108
+ * @return void
1109
+ *
1110
+ *
1111
+ * ### Providing read access to the underlying value
1112
+ *
1113
+ * #### Variation 1
1114
+ *
1115
+ * In this variation, the user provides a function that returns a Variant with the current value.
1116
+ *
1117
+ * The sourceTimestamp will be set automatically.
1118
+ *
1119
+ * The get function is called synchronously.
1120
+ *
1121
+ * @example
1122
+ *
1123
+ *
1124
+ * ```javascript
1125
+ * ...
1126
+ * var options = {
1127
+ * get : () => {
1128
+ * return new Variant({...});
1129
+ * },
1130
+ * set : function(variant) {
1131
+ * // store the variant somewhere
1132
+ * return StatusCodes.Good;
1133
+ * }
1134
+ * };
1135
+ * ...
1136
+ * engine.bindVariable(nodeId,options):
1137
+ * ...
1138
+ * ```
1139
+ *
1140
+ *
1141
+ * #### Variation 2:
1142
+ *
1143
+ * This variation can be used when the user wants to specify a specific '''sourceTimestamp''' associated
1144
+ * with the current value of the UAVariable.
1145
+ *
1146
+ * The provided ```timestamped_get``` function should return an object with three properties:
1147
+ * * value: containing the variant value or a error StatusCode,
1148
+ * * sourceTimestamp
1149
+ * * sourcePicoseconds
1150
+ *
1151
+ * ```javascript
1152
+ * ...
1153
+ * var myDataValue = new DataValue({
1154
+ * value: {dataType: DataType.Double , value: 10.0},
1155
+ * sourceTimestamp : new Date(),
1156
+ * sourcePicoseconds: 0
1157
+ * });
1158
+ * ...
1159
+ * var options = {
1160
+ * timestamped_get : () => { return myDataValue; }
1161
+ * };
1162
+ * ...
1163
+ * engine.bindVariable(nodeId,options):
1164
+ * ...
1165
+ * // record a new value
1166
+ * myDataValue.value.value = 5.0;
1167
+ * myDataValue.sourceTimestamp = new Date();
1168
+ * ...
1169
+ * ```
1170
+ *
1171
+ *
1172
+ * #### Variation 3:
1173
+ *
1174
+ * This variation can be used when the value associated with the variables requires a asynchronous function call to be
1175
+ * extracted. In this case, the user should provide an async method ```refreshFunc```.
1176
+ *
1177
+ *
1178
+ * The ```refreshFunc``` shall do whatever is necessary to fetch the most up to date version of the variable value, and
1179
+ * call the ```callback``` function when the data is ready.
1180
+ *
1181
+ *
1182
+ * The ```callback``` function follow the standard callback function signature:
1183
+ * * the first argument shall be **null** or **Error**, depending of the outcome of the fetch operation,
1184
+ * * the second argument shall be a DataValue with the new UAVariable Value, a StatusCode, and time stamps.
1185
+ *
1186
+ *
1187
+ * Optionally, it is possible to pass a sourceTimestamp and a sourcePicoseconds value as a third and fourth arguments
1188
+ * of the callback. When sourceTimestamp and sourcePicoseconds are missing, the system will set their default value
1189
+ * to the current time..
1190
+ *
1191
+ *
1192
+ * ```javascript
1193
+ * ...
1194
+ * var options = {
1195
+ * refreshFunc : function(callback) {
1196
+ * ... do_some_async_stuff_to_get_the_new_variable_value
1197
+ * var dataValue = new DataValue({
1198
+ * value: new Variant({...}),
1199
+ * statusCode: StatusCodes.Good,
1200
+ * sourceTimestamp: new Date()
1201
+ * });
1202
+ * callback(null,dataValue);
1203
+ * }
1204
+ * };
1205
+ * ...
1206
+ * variable.bindVariable(nodeId,options):
1207
+ * ...
1208
+ * ```
1209
+ *
1210
+ * ### Providing write access to the underlying value
1211
+ *
1212
+ * #### Variation1 - provide a simple synchronous set function
1213
+ *
1214
+ *
1215
+ * #### Notes
1216
+ * to do : explain return StatusCodes.GoodCompletesAsynchronously;
1217
+ *
1218
+ */
1219
+ public bindVariable(options?: BindVariableOptions, overwrite?: boolean): void {
1220
+ if (overwrite) {
1221
+ this._timestamped_set_func = null;
1222
+ this._timestamped_get_func = null;
1223
+ this._get_func = null;
1224
+ this._set_func = null;
1225
+ this.refreshFunc = undefined;
1226
+ this._historyRead = UAVariableImpl.prototype._historyRead;
1227
+ }
1223
1228
 
1224
- const _historyRead = options.historyRead;
1225
- if (_historyRead) {
1226
- assert(typeof this._historyRead !== "function" || this._historyRead === UAVariableImpl.prototype._historyRead);
1227
- assert(typeof _historyRead === "function");
1229
+ options = options || {};
1228
1230
 
1229
- this._historyRead = _historyRead;
1230
- assert(this._historyRead.length === 6);
1231
- }
1232
- // post conditions
1233
- assert(typeof this._timestamped_set_func === "function");
1234
- assert(this._timestamped_set_func!.length === 2, "expecting 2 parameters");
1235
- }
1231
+ assert(typeof this._timestamped_set_func !== "function", "UAVariable already bound");
1232
+ assert(typeof this._timestamped_get_func !== "function", "UAVariable already bound");
1236
1233
 
1237
- /**
1238
- * @method readValueAsync
1239
- */
1240
- public readValueAsync(context: ISessionContext | null, callback: CallbackT<DataValue>): void;
1241
- public readValueAsync(context: ISessionContext | null): Promise<DataValue>;
1242
- public readValueAsync(context: ISessionContext | null, callback?: CallbackT<DataValue>): any {
1243
- if (!context) {
1244
- context = SessionContext.defaultContext;
1245
- }
1246
- assert(typeof callback === "function");
1234
+ bind_getter.call(this, options);
1235
+ bind_setter.call(this, options);
1247
1236
 
1248
- this.__waiting_callbacks = this.__waiting_callbacks || [];
1249
- this.__waiting_callbacks.push(callback);
1237
+ const _historyRead = options.historyRead;
1238
+ if (_historyRead) {
1239
+ assert(typeof this._historyRead !== "function" || this._historyRead === UAVariableImpl.prototype._historyRead);
1240
+ assert(typeof _historyRead === "function");
1250
1241
 
1251
- const _readValueAsync_in_progress = this.__waiting_callbacks.length >= 2;
1252
- if (_readValueAsync_in_progress) {
1253
- return;
1254
- }
1242
+ this._historyRead = _historyRead;
1243
+ assert(this._historyRead.length === 6);
1244
+ }
1245
+ // post conditions
1246
+ assert(typeof this._timestamped_set_func === "function");
1247
+ assert(this._timestamped_set_func!.length === 2, "expecting 2 parameters");
1248
+ }
1249
+
1250
+ /**
1251
+ * @method readValueAsync
1252
+ */
1253
+ public readValueAsync(context: ISessionContext | null, callback: CallbackT<DataValue>): void;
1254
+ public readValueAsync(context: ISessionContext | null): Promise<DataValue>;
1255
+ public readValueAsync(context: ISessionContext | null, callback?: CallbackT<DataValue>): any {
1256
+ if (!context) {
1257
+ context = SessionContext.defaultContext;
1258
+ }
1259
+ assert(typeof callback === "function");
1255
1260
 
1256
- const readImmediate = (innerCallback: (err: Error | null, dataValue: DataValue) => void) => {
1257
- assert(this.$dataValue instanceof DataValue);
1258
- const dataValue = this.readValue();
1259
- innerCallback(null, dataValue);
1260
- };
1261
+ this.__waiting_callbacks = this.__waiting_callbacks || [];
1262
+ this.__waiting_callbacks.push(callback);
1261
1263
 
1262
- let func: (innerCallback: (err: Error | null, dataValue: DataValue) => void) => void;
1264
+ const _readValueAsync_in_progress = this.__waiting_callbacks.length >= 2;
1265
+ if (_readValueAsync_in_progress) {
1266
+ return;
1267
+ }
1263
1268
 
1264
- if (!this.isReadable(context)) {
1265
- func = (innerCallback: (err: Error | null, dataValue: DataValue) => void) => {
1266
- const dataValue = new DataValue({ statusCode: StatusCodes.BadNotReadable });
1269
+ const readImmediate = (innerCallback: (err: Error | null, dataValue: DataValue) => void) => {
1270
+ assert(this.$dataValue instanceof DataValue);
1271
+ const dataValue = this.readValue();
1267
1272
  innerCallback(null, dataValue);
1268
1273
  };
1269
- } else if (!this.checkPermissionPrivate(context, PermissionType.Read)) {
1270
- func = (innerCallback: (err: Error | null, dataValue: DataValue) => void) => {
1271
- const dataValue = new DataValue({ statusCode: StatusCodes.BadUserAccessDenied });
1272
- innerCallback(null, dataValue);
1273
- };
1274
- } else if (!this.isUserReadable(context)) {
1275
- func = (innerCallback: (err: Error | null, dataValue: DataValue) => void) => {
1276
- const dataValue = new DataValue({ statusCode: StatusCodes.BadNotReadable });
1277
- innerCallback(null, dataValue);
1278
- };
1279
- } else {
1280
- func = typeof this.refreshFunc === "function" ? this.asyncRefresh.bind(this, new Date()) : readImmediate;
1281
- }
1282
-
1283
- const satisfy_callbacks = (err: Error | null, dataValue?: DataValue) => {
1284
- // now call all pending callbacks
1285
- const callbacks = this.__waiting_callbacks || [];
1286
- this.__waiting_callbacks = [];
1287
- const n = callbacks.length;
1288
- for (const callback1 of callbacks) {
1289
- callback1.call(this, err, dataValue);
1290
1274
 
1275
+ let func: (innerCallback: (err: Error | null, dataValue: DataValue) => void) => void;
1276
+
1277
+ if (!this.isReadable(context)) {
1278
+ func = (innerCallback: (err: Error | null, dataValue: DataValue) => void) => {
1279
+ const dataValue = new DataValue({ statusCode: StatusCodes.BadNotReadable });
1280
+ innerCallback(null, dataValue);
1281
+ };
1282
+ } else if (!this.checkPermissionPrivate(context, PermissionType.Read)) {
1283
+ func = (innerCallback: (err: Error | null, dataValue: DataValue) => void) => {
1284
+ const dataValue = new DataValue({ statusCode: StatusCodes.BadUserAccessDenied });
1285
+ innerCallback(null, dataValue);
1286
+ };
1287
+ } else if (!this.isUserReadable(context)) {
1288
+ func = (innerCallback: (err: Error | null, dataValue: DataValue) => void) => {
1289
+ const dataValue = new DataValue({ statusCode: StatusCodes.BadNotReadable });
1290
+ innerCallback(null, dataValue);
1291
+ };
1292
+ } else {
1293
+ func = typeof this.refreshFunc === "function" ? this.asyncRefresh.bind(this, new Date()) : readImmediate;
1291
1294
  }
1292
- };
1293
1295
 
1294
- try {
1295
- func.call(this, satisfy_callbacks);
1296
- } catch (err) {
1297
- // istanbul ignore next
1298
- if (doDebug) {
1299
- debugLog(chalk.red("func readValueAsync has failed "));
1300
- if (err instanceof Error) {
1301
- debugLog(" stack", err.stack);
1296
+ const satisfy_callbacks = (err: Error | null, dataValue?: DataValue) => {
1297
+ // now call all pending callbacks
1298
+ const callbacks = this.__waiting_callbacks || [];
1299
+ this.__waiting_callbacks = [];
1300
+ const n = callbacks.length;
1301
+ for (const callback1 of callbacks) {
1302
+ callback1.call(this, err, dataValue);
1303
+
1304
+ }
1305
+ };
1306
+
1307
+ try {
1308
+ func.call(this, satisfy_callbacks);
1309
+ } catch (err) {
1310
+ // istanbul ignore next
1311
+ if (doDebug) {
1312
+ debugLog(chalk.red("func readValueAsync has failed "));
1313
+ if (err instanceof Error) {
1314
+ debugLog(" stack", err.stack);
1315
+ }
1302
1316
  }
1317
+ satisfy_callbacks(err as Error);
1303
1318
  }
1304
- satisfy_callbacks(err as Error);
1305
1319
  }
1306
- }
1307
1320
 
1308
- public getWriteMask(): number {
1309
- return super.getWriteMask();
1310
- }
1321
+ public getWriteMask(): number {
1322
+ return super.getWriteMask();
1323
+ }
1311
1324
 
1312
- public getUserWriteMask(): number {
1313
- return super.getUserWriteMask();
1314
- }
1325
+ public getUserWriteMask(): number {
1326
+ return super.getUserWriteMask();
1327
+ }
1315
1328
 
1316
- public clone(options: CloneOptions, optionalFilter?: CloneFilter, extraInfo?: CloneExtraInfo): UAVariable {
1317
- options = {
1318
- ...options,
1319
- // check this eventNotifier: this.eventNotifier,
1320
- // check this symbolicName: this.symbolicName,
1321
-
1322
- accessLevel: this.accessLevel,
1323
- arrayDimensions: this.arrayDimensions,
1324
- dataType: this.dataType,
1325
- historizing: this.historizing,
1326
- minimumSamplingInterval: this.minimumSamplingInterval,
1327
- userAccessLevel: this.userAccessLevel,
1328
- valueRank: this.valueRank
1329
- };
1329
+ public clone(options: CloneOptions, optionalFilter?: CloneFilter, extraInfo?: CloneExtraInfo): UAVariable {
1330
+ options = {
1331
+ ...options,
1332
+ // check this eventNotifier: this.eventNotifier,
1333
+ // check this symbolicName: this.symbolicName,
1330
1334
 
1331
- const newVariable = _clone.call(
1332
- this,
1333
- UAVariableImpl,
1334
- options,
1335
- optionalFilter || defaultCloneFilter,
1336
- extraInfo || defaultCloneExtraInfo
1337
- ) as UAVariableImpl;
1335
+ accessLevel: this.accessLevel,
1336
+ arrayDimensions: this.arrayDimensions,
1337
+ dataType: this.dataType,
1338
+ historizing: this.historizing,
1339
+ minimumSamplingInterval: this.minimumSamplingInterval,
1340
+ userAccessLevel: this.userAccessLevel,
1341
+ valueRank: this.valueRank
1342
+ };
1338
1343
 
1339
- newVariable.bindVariable();
1344
+ const newVariable = _clone.call(
1345
+ this,
1346
+ UAVariableImpl,
1347
+ options,
1348
+ optionalFilter || defaultCloneFilter,
1349
+ extraInfo || defaultCloneExtraInfo
1350
+ ) as UAVariableImpl;
1340
1351
 
1341
- assert(typeof newVariable._timestamped_set_func === "function");
1352
+ newVariable.bindVariable();
1342
1353
 
1343
- assert(newVariable.dataType === this.dataType);
1344
- newVariable.$dataValue = this.$dataValue.clone();
1354
+ assert(typeof newVariable._timestamped_set_func === "function");
1345
1355
 
1346
- // also bind extension object
1347
- const v = newVariable.$dataValue.value;
1348
- if (v.dataType === DataType.ExtensionObject && v.value && v.arrayType === VariantArrayType.Scalar) {
1349
- try {
1350
- newVariable.bindExtensionObject(newVariable.$dataValue.value.value);
1351
- } catch (err) {
1352
- errorLog("Errro binding extension objects");
1353
- errorLog((err as Error).message);
1354
- errorLog(this.toString());
1355
- errorLog("---------------------------------------");
1356
- errorLog(this.$dataValue.toString());
1357
- errorLog("---------------------------------------");
1358
- errorLog(newVariable.$dataValue.toString());
1359
- throw err;
1356
+ assert(newVariable.dataType === this.dataType);
1357
+ newVariable.$dataValue = this.$dataValue.clone();
1358
+
1359
+ // also bind extension object
1360
+ const v = newVariable.$dataValue.value;
1361
+ if (v.dataType === DataType.ExtensionObject && v.value && v.arrayType === VariantArrayType.Scalar) {
1362
+ try {
1363
+ newVariable.bindExtensionObject(newVariable.$dataValue.value.value);
1364
+ } catch (err) {
1365
+ errorLog("Errro binding extension objects");
1366
+ errorLog((err as Error).message);
1367
+ errorLog(this.toString());
1368
+ errorLog("---------------------------------------");
1369
+ errorLog(this.$dataValue.toString());
1370
+ errorLog("---------------------------------------");
1371
+ errorLog(newVariable.$dataValue.toString());
1372
+ throw err;
1373
+ }
1360
1374
  }
1375
+ return newVariable;
1361
1376
  }
1362
- return newVariable;
1363
- }
1364
1377
 
1365
- public getDataTypeNode(): UADataType {
1366
- const addressSpace = this.addressSpace;
1367
- const dt = addressSpace.findNode(this.dataType);
1368
- // istanbul ignore next
1369
- if (!dt) {
1370
- throw new Error("getDataTypeNode: cannot find dataType " + this.dataType.toString());
1378
+ public getDataTypeNode(): UADataType {
1379
+ const addressSpace = this.addressSpace;
1380
+ const dt = addressSpace.findNode(this.dataType);
1381
+ // istanbul ignore next
1382
+ if (!dt) {
1383
+ throw new Error("getDataTypeNode: cannot find dataType " + this.dataType.toString());
1384
+ }
1385
+ return dt as UADataType;
1371
1386
  }
1372
- return dt as UADataType;
1373
- }
1374
-
1375
- public get dataTypeObj(): UADataType {
1376
- return this.getDataTypeNode();
1377
- }
1378
1387
 
1379
- public checkExtensionObjectIsCorrect(extObj: ExtensionObject | ExtensionObject[] | null): boolean {
1380
- if (!extObj) {
1381
- return true;
1388
+ public get dataTypeObj(): UADataType {
1389
+ return this.getDataTypeNode();
1382
1390
  }
1383
- const addressSpace = this.addressSpace;
1384
- const dataType = addressSpace.findDataType(this.dataType);
1385
- if (!dataType) {
1386
- // may be we are in the process of loading a xml file and the corresponding dataType
1387
- // has not yet been loaded !
1388
- return true;
1389
- }
1390
-
1391
- const Constructor = addressSpace.getExtensionObjectConstructor(this.dataType);
1392
1391
 
1393
- if (this.valueRank === -1) {
1394
- /** Scalar */
1395
- if (extObj instanceof Array) {
1396
- return false;
1392
+ public checkExtensionObjectIsCorrect(extObj: ExtensionObject | ExtensionObject[] | null): boolean {
1393
+ if (!extObj) {
1394
+ return true;
1397
1395
  }
1398
- return checkExtensionObjectIsCorrectScalar.call(this, extObj);
1399
- } else if (this.valueRank >= 1) {
1400
- /** array */
1401
- if (!(extObj instanceof Array)) {
1402
- // let's coerce this scalar into an 1-element array if it is a valid extension object
1403
- if (checkExtensionObjectIsCorrectScalar.call(this, extObj)) {
1404
- warningLog(
1405
- `warning: checkExtensionObjectIsCorrect : expecting a array but got a scalar (value rank of '${this.browseName.toString()}' is 1)\nautomatic conversion from scalar to array with 1 element is taking place.`
1406
- );
1407
- extObj = [extObj];
1408
- } else {
1396
+ const addressSpace = this.addressSpace;
1397
+ const dataType = addressSpace.findDataType(this.dataType);
1398
+ if (!dataType) {
1399
+ // may be we are in the process of loading a xml file and the corresponding dataType
1400
+ // has not yet been loaded !
1401
+ return true;
1402
+ }
1403
+
1404
+ const Constructor = addressSpace.getExtensionObjectConstructor(this.dataType);
1405
+
1406
+ if (this.valueRank === -1) {
1407
+ /** Scalar */
1408
+ if (extObj instanceof Array) {
1409
1409
  return false;
1410
1410
  }
1411
+ return checkExtensionObjectIsCorrectScalar.call(this, extObj);
1412
+ } else if (this.valueRank >= 1) {
1413
+ /** array */
1414
+ if (!(extObj instanceof Array)) {
1415
+ // let's coerce this scalar into an 1-element array if it is a valid extension object
1416
+ if (checkExtensionObjectIsCorrectScalar.call(this, extObj)) {
1417
+ warningLog(
1418
+ `warning: checkExtensionObjectIsCorrect : expecting a array but got a scalar (value rank of '${this.browseName.toString()}' is 1)\nautomatic conversion from scalar to array with 1 element is taking place.`
1419
+ );
1420
+ extObj = [extObj];
1421
+ } else {
1422
+ return false;
1423
+ }
1424
+ }
1425
+ return checkExtensionObjectIsCorrectArray.call(this, extObj);
1426
+ } else if (this.valueRank === 0) {
1427
+ // Scalar or Array
1428
+ const isCorrectScalar = !Array.isArray(extObj) && checkExtensionObjectIsCorrectScalar.call(this, extObj);
1429
+ const isCorrectArray =
1430
+ Array.isArray(extObj) && checkExtensionObjectIsCorrectArray.call(this, extObj as ExtensionObject[]);
1431
+ return isCorrectArray || isCorrectScalar;
1432
+ } else {
1433
+ throw new Error(
1434
+ `checkExtensionObjectIsCorrect: Not Implemented case, please contact sterfive : this.valueRank =${this.valueRank}`
1435
+ );
1411
1436
  }
1412
- return checkExtensionObjectIsCorrectArray.call(this, extObj);
1413
- } else if (this.valueRank === 0) {
1414
- // Scalar or Array
1415
- const isCorrectScalar = !Array.isArray(extObj) && checkExtensionObjectIsCorrectScalar.call(this, extObj);
1416
- const isCorrectArray =
1417
- Array.isArray(extObj) && checkExtensionObjectIsCorrectArray.call(this, extObj as ExtensionObject[]);
1418
- return isCorrectArray || isCorrectScalar;
1419
- } else {
1420
- throw new Error(
1421
- `checkExtensionObjectIsCorrect: Not Implemented case, please contact sterfive : this.valueRank =${this.valueRank}`
1422
- );
1423
- }
1424
- function checkExtensionObjectIsCorrectScalar(
1425
- this: UAVariableImpl,
1426
- extObj: ExtensionObject | ExtensionObject[] | null
1427
- ): boolean {
1428
- // istanbul ignore next
1429
- if (!(extObj && extObj.constructor)) {
1430
- errorLog(extObj);
1431
- throw new Error("expecting an valid extension object");
1432
- }
1433
- return extObj.constructor.name === Constructor.name;
1434
- }
1435
-
1436
- function checkExtensionObjectIsCorrectArray(this: UAVariableImpl, extObjArray: ExtensionObject[]): boolean {
1437
- // istanbul ignore next
1438
- for (const extObj of extObjArray) {
1437
+ function checkExtensionObjectIsCorrectScalar(
1438
+ this: UAVariableImpl,
1439
+ extObj: ExtensionObject | ExtensionObject[] | null
1440
+ ): boolean {
1441
+ // istanbul ignore next
1439
1442
  if (!(extObj && extObj.constructor)) {
1440
1443
  errorLog(extObj);
1441
1444
  throw new Error("expecting an valid extension object");
1442
1445
  }
1446
+ return extObj.constructor.name === Constructor.name;
1443
1447
  }
1444
- try {
1445
- for (const e of extObjArray) {
1446
- if (!e) {
1447
- continue;
1448
+
1449
+ function checkExtensionObjectIsCorrectArray(this: UAVariableImpl, extObjArray: ExtensionObject[]): boolean {
1450
+ // istanbul ignore next
1451
+ for (const extObj of extObjArray) {
1452
+ if (!(extObj && extObj.constructor)) {
1453
+ errorLog(extObj);
1454
+ throw new Error("expecting an valid extension object");
1448
1455
  }
1449
- if (e.constructor.name !== Constructor.name) {
1450
- debugLog("extObj.constructor.name ", e.constructor.name, "expected", Constructor.name);
1451
- return false;
1456
+ }
1457
+ try {
1458
+ for (const e of extObjArray) {
1459
+ if (!e) {
1460
+ continue;
1461
+ }
1462
+ if (e.constructor.name !== Constructor.name) {
1463
+ debugLog("extObj.constructor.name ", e.constructor.name, "expected", Constructor.name);
1464
+ return false;
1465
+ }
1452
1466
  }
1467
+ return true;
1468
+ } catch (err) {
1469
+ errorLog(err);
1470
+ return false;
1453
1471
  }
1454
- return true;
1455
- } catch (err) {
1456
- errorLog(err);
1457
- return false;
1458
1472
  }
1459
1473
  }
1460
- }
1461
1474
 
1462
- /**
1463
- * @private
1464
- * install UAVariable to exposed th
1465
- *
1466
- * precondition:
1467
- */
1468
- public installExtensionObjectVariables(): void {
1469
- _installExtensionObjectBindingOnProperties(this, { createMissingProp: true });
1470
- }
1471
- /**
1472
- * @method bindExtensionObject
1473
- * @return {ExtensionObject}
1474
- */
1475
- public bindExtensionObjectScalar(
1476
- optionalExtensionObject?: ExtensionObject,
1477
- options?: BindExtensionObjectOptions
1478
- ): ExtensionObject | null {
1479
- assert(this.valueRank === -1, "expecting an Scalar variable here");
1480
- return _bindExtensionObject(this, optionalExtensionObject, options) as ExtensionObject;
1481
- }
1482
-
1483
- public bindExtensionObjectArray(
1484
- optionalExtensionObject?: ExtensionObject[],
1485
- options?: BindExtensionObjectOptions
1486
- ): ExtensionObject[] | null {
1487
- assert(this.valueRank >= 1, "expecting an Array or a Matrix variable here");
1488
- return _bindExtensionObjectArrayOrMatrix(this, optionalExtensionObject, options);
1489
- }
1490
-
1491
- public bindExtensionObject(
1492
- optionalExtensionObject?: ExtensionObject | ExtensionObject[],
1493
- options?: BindExtensionObjectOptions
1494
- ): ExtensionObject | ExtensionObject[] | null {
1495
- if (optionalExtensionObject) {
1496
- if (optionalExtensionObject instanceof Array) {
1497
- assert(this.valueRank >= 1, "expecting an Array of Matrix variable here");
1498
- return _bindExtensionObjectArrayOrMatrix(this, optionalExtensionObject, options);
1499
- } else {
1500
- assert(this.valueRank === -1, "expecting an Scalar variable here");
1501
- return _bindExtensionObject(this, optionalExtensionObject, options) as ExtensionObject;
1475
+ /**
1476
+ * @private
1477
+ * install UAVariable to exposed th
1478
+ *
1479
+ * precondition:
1480
+ */
1481
+ public installExtensionObjectVariables(): void {
1482
+ _installExtensionObjectBindingOnProperties(this, { createMissingProp: true });
1483
+ }
1484
+ /**
1485
+ * @method bindExtensionObject
1486
+ * @return {ExtensionObject}
1487
+ */
1488
+ public bindExtensionObjectScalar(
1489
+ optionalExtensionObject?: ExtensionObject,
1490
+ options?: BindExtensionObjectOptions
1491
+ ): ExtensionObject | null {
1492
+ assert(this.valueRank === -1, "expecting an Scalar variable here");
1493
+ return _bindExtensionObject(this, optionalExtensionObject, options) as ExtensionObject;
1494
+ }
1495
+
1496
+ public bindExtensionObjectArray(
1497
+ optionalExtensionObject?: ExtensionObject[],
1498
+ options?: BindExtensionObjectOptions
1499
+ ): ExtensionObject[] | null {
1500
+ assert(this.valueRank >= 1, "expecting an Array or a Matrix variable here");
1501
+ return _bindExtensionObjectArrayOrMatrix(this, optionalExtensionObject, options);
1502
+ }
1503
+
1504
+ public bindExtensionObject(
1505
+ optionalExtensionObject?: ExtensionObject | ExtensionObject[],
1506
+ options?: BindExtensionObjectOptions
1507
+ ): ExtensionObject | ExtensionObject[] | null {
1508
+
1509
+ // coerce to ExtensionObject[] when this.valueRank === 1
1510
+ if (optionalExtensionObject && this.valueRank === 1 && !Array.isArray(optionalExtensionObject) && optionalExtensionObject instanceof ExtensionObject) {
1511
+ warningLog("bindExtensionObject: coerce to ExtensionObject[] when this.valueRank === 1");
1512
+ optionalExtensionObject = [optionalExtensionObject];
1502
1513
  }
1503
- }
1504
- assert(optionalExtensionObject === undefined);
1505
- if (this.valueRank === -1) {
1506
- return _bindExtensionObject(this, undefined, options) as ExtensionObject;
1507
- } else if (this.valueRank === 1) {
1508
- return _bindExtensionObjectArrayOrMatrix(this, undefined, options);
1509
- } else if (this.valueRank > 1) {
1510
- return _bindExtensionObjectArrayOrMatrix(this, undefined, options);
1511
- }
1512
- // unsupported case ...
1513
- return null;
1514
- }
1515
-
1516
- public updateExtensionObjectPartial(partialExtensionObject?: { [key: string]: any }): ExtensionObject {
1517
- setExtensionObjectPartialValue(this, partialExtensionObject);
1518
- return this.$extensionObject;
1519
- }
1520
-
1521
- public incrementExtensionObjectPartial(path: string | string[]): void {
1522
- const extensionObject = this.readValue().value.value as ExtensionObject;
1523
- const partialData = extractPartialData(path, extensionObject);
1524
- incrementElement(path, partialData);
1525
- setExtensionObjectPartialValue(this, partialData);
1526
- }
1527
-
1528
- public toString(): string {
1529
- const options = new ToStringBuilder();
1530
- UAVariable_toString.call(this, options);
1531
- return options.toString();
1532
- }
1533
-
1534
- // ---------------------------------------------------------------------------------------------------
1535
- // History
1536
- // ---------------------------------------------------------------------------------------------------
1537
-
1538
- public historyRead(
1539
- context: ISessionContext,
1540
- historyReadDetails:
1541
- | HistoryReadDetails
1542
- | ReadRawModifiedDetails
1543
- | ReadEventDetails
1544
- | ReadProcessedDetails
1545
- | ReadAtTimeDetails,
1546
- indexRange: NumericRange | null,
1547
- dataEncoding: QualifiedNameLike | null,
1548
- continuationData: ContinuationData
1549
- ): Promise<HistoryReadResult>;
1550
-
1551
- public historyRead(
1552
- context: ISessionContext,
1553
- historyReadDetails:
1554
- | HistoryReadDetails
1555
- | ReadRawModifiedDetails
1556
- | ReadEventDetails
1557
- | ReadProcessedDetails
1558
- | ReadAtTimeDetails,
1559
- indexRange: NumericRange | null,
1560
- dataEncoding: QualifiedNameLike | null,
1561
- continuationData: ContinuationData,
1562
- callback: CallbackT<HistoryReadResult>
1563
- ): void;
1564
- public historyRead(
1565
- context: ISessionContext,
1566
- historyReadDetails:
1567
- | HistoryReadDetails
1568
- | ReadRawModifiedDetails
1569
- | ReadEventDetails
1570
- | ReadProcessedDetails
1571
- | ReadAtTimeDetails,
1572
- indexRange: NumericRange | null,
1573
- dataEncoding: QualifiedNameLike | null,
1574
- continuationData: ContinuationData,
1575
- callback?: CallbackT<HistoryReadResult>
1576
- ): any {
1577
- assert(context instanceof SessionContext);
1578
- assert(typeof callback === "function");
1579
- if (typeof this._historyRead !== "function") {
1580
- return callback!(null, new HistoryReadResult({ statusCode: StatusCodes.BadNotReadable }));
1581
- }
1582
-
1583
- this._historyRead(context, historyReadDetails, indexRange, dataEncoding, continuationData, callback!);
1584
- }
1585
-
1586
- public _historyReadRaw(
1587
- context: ISessionContext,
1588
- historyReadRawModifiedDetails: ReadRawModifiedDetails,
1589
- indexRange: NumericRange | null,
1590
- dataEncoding: QualifiedNameLike | null,
1591
- continuationData: ContinuationData,
1592
- callback: CallbackT<HistoryReadResult>
1593
- ): void {
1594
- throw new Error("");
1595
- }
1596
1514
 
1597
- public _historyReadRawModify(
1598
- context: ISessionContext,
1599
- historyReadRawModifiedDetails: ReadRawModifiedDetails,
1600
- indexRange: NumericRange | null,
1601
- dataEncoding: QualifiedNameLike | null,
1602
- continuationData: ContinuationData,
1603
- callback?: CallbackT<HistoryReadResult>
1604
- ): any {
1605
- throw new Error("");
1606
- }
1515
+ if (optionalExtensionObject) {
1516
+ if (optionalExtensionObject instanceof Array) {
1517
+ assert(this.valueRank >= 1, "bindExtensionObject: expecting an Array of Matrix variable here");
1518
+ return _bindExtensionObjectArrayOrMatrix(this, optionalExtensionObject, options);
1519
+ } else {
1520
+ if (this.valueRank !== -1 && this.valueRank !== 0) {
1521
+ throw new Error("bindExtensionObject: expecting an Scalar variable here but got value rank " + this.valueRank);
1522
+ }
1523
+ return _bindExtensionObject(this, optionalExtensionObject, options) as ExtensionObject;
1524
+ }
1525
+ }
1526
+ assert(optionalExtensionObject === undefined);
1527
+ if (this.valueRank === -1) {
1528
+ return _bindExtensionObject(this, undefined, options) as ExtensionObject;
1529
+ } else if (this.valueRank === 1) {
1530
+ return _bindExtensionObjectArrayOrMatrix(this, undefined, options);
1531
+ } else if (this.valueRank > 1) {
1532
+ return _bindExtensionObjectArrayOrMatrix(this, undefined, options);
1533
+ }
1534
+ // unsupported case ...
1535
+ return null;
1536
+ }
1537
+
1538
+ public updateExtensionObjectPartial(partialExtensionObject?: { [key: string]: any }): ExtensionObject {
1539
+ setExtensionObjectPartialValue(this, partialExtensionObject);
1540
+ return this.$extensionObject;
1541
+ }
1542
+
1543
+ public incrementExtensionObjectPartial(path: string | string[]): void {
1544
+ const extensionObject = this.readValue().value.value as ExtensionObject;
1545
+ const partialData = extractPartialData(path, extensionObject);
1546
+ incrementElement(path, partialData);
1547
+ setExtensionObjectPartialValue(this, partialData);
1548
+ }
1549
+
1550
+ public toString(): string {
1551
+ const options = new ToStringBuilder();
1552
+ UAVariable_toString.call(this, options);
1553
+ return options.toString();
1554
+ }
1555
+
1556
+ // ---------------------------------------------------------------------------------------------------
1557
+ // History
1558
+ // ---------------------------------------------------------------------------------------------------
1559
+
1560
+ public historyRead(
1561
+ context: ISessionContext,
1562
+ historyReadDetails:
1563
+ | HistoryReadDetails
1564
+ | ReadRawModifiedDetails
1565
+ | ReadEventDetails
1566
+ | ReadProcessedDetails
1567
+ | ReadAtTimeDetails,
1568
+ indexRange: NumericRange | null,
1569
+ dataEncoding: QualifiedNameLike | null,
1570
+ continuationData: ContinuationData
1571
+ ): Promise<HistoryReadResult>;
1572
+
1573
+ public historyRead(
1574
+ context: ISessionContext,
1575
+ historyReadDetails:
1576
+ | HistoryReadDetails
1577
+ | ReadRawModifiedDetails
1578
+ | ReadEventDetails
1579
+ | ReadProcessedDetails
1580
+ | ReadAtTimeDetails,
1581
+ indexRange: NumericRange | null,
1582
+ dataEncoding: QualifiedNameLike | null,
1583
+ continuationData: ContinuationData,
1584
+ callback: CallbackT<HistoryReadResult>
1585
+ ): void;
1586
+ public historyRead(
1587
+ context: ISessionContext,
1588
+ historyReadDetails:
1589
+ | HistoryReadDetails
1590
+ | ReadRawModifiedDetails
1591
+ | ReadEventDetails
1592
+ | ReadProcessedDetails
1593
+ | ReadAtTimeDetails,
1594
+ indexRange: NumericRange | null,
1595
+ dataEncoding: QualifiedNameLike | null,
1596
+ continuationData: ContinuationData,
1597
+ callback?: CallbackT<HistoryReadResult>
1598
+ ): any {
1599
+ assert(context instanceof SessionContext);
1600
+ assert(typeof callback === "function");
1601
+ if (typeof this._historyRead !== "function") {
1602
+ return callback!(null, new HistoryReadResult({ statusCode: StatusCodes.BadNotReadable }));
1603
+ }
1607
1604
 
1608
- public _historyRead(
1609
- context: ISessionContext,
1610
- historyReadDetails:
1611
- | HistoryReadDetails
1612
- | ReadRawModifiedDetails
1613
- | ReadEventDetails
1614
- | ReadProcessedDetails
1615
- | ReadAtTimeDetails,
1616
- indexRange: NumericRange | null,
1617
- dataEncoding: QualifiedNameLike | null,
1618
- continuationData: ContinuationData,
1619
- callback: CallbackT<HistoryReadResult>
1620
- ): void {
1621
-
1622
- if (!this.checkPermissionPrivate(context, PermissionType.ReadHistory)) {
1623
- const result = new HistoryReadResult({
1624
- statusCode: StatusCodes.BadUserAccessDenied
1625
- });
1626
- callback(null, result);
1627
- }
1628
- if (!this.canUserReadHistory(context)) {
1605
+ this._historyRead(context, historyReadDetails, indexRange, dataEncoding, continuationData, callback!);
1606
+ }
1607
+
1608
+ public _historyReadRaw(
1609
+ context: ISessionContext,
1610
+ historyReadRawModifiedDetails: ReadRawModifiedDetails,
1611
+ indexRange: NumericRange | null,
1612
+ dataEncoding: QualifiedNameLike | null,
1613
+ continuationData: ContinuationData,
1614
+ callback: CallbackT<HistoryReadResult>
1615
+ ): void {
1616
+ throw new Error("");
1617
+ }
1618
+
1619
+ public _historyReadRawModify(
1620
+ context: ISessionContext,
1621
+ historyReadRawModifiedDetails: ReadRawModifiedDetails,
1622
+ indexRange: NumericRange | null,
1623
+ dataEncoding: QualifiedNameLike | null,
1624
+ continuationData: ContinuationData,
1625
+ callback?: CallbackT<HistoryReadResult>
1626
+ ): any {
1627
+ throw new Error("");
1628
+ }
1629
+
1630
+ public _historyRead(
1631
+ context: ISessionContext,
1632
+ historyReadDetails:
1633
+ | HistoryReadDetails
1634
+ | ReadRawModifiedDetails
1635
+ | ReadEventDetails
1636
+ | ReadProcessedDetails
1637
+ | ReadAtTimeDetails,
1638
+ indexRange: NumericRange | null,
1639
+ dataEncoding: QualifiedNameLike | null,
1640
+ continuationData: ContinuationData,
1641
+ callback: CallbackT<HistoryReadResult>
1642
+ ): void {
1643
+
1644
+ if (!this.checkPermissionPrivate(context, PermissionType.ReadHistory)) {
1645
+ const result = new HistoryReadResult({
1646
+ statusCode: StatusCodes.BadUserAccessDenied
1647
+ });
1648
+ callback(null, result);
1649
+ }
1650
+ if (!this.canUserReadHistory(context)) {
1651
+ const result = new HistoryReadResult({
1652
+ statusCode: StatusCodes.BadHistoryOperationUnsupported
1653
+ });
1654
+ callback(null, result);
1655
+ }
1629
1656
  const result = new HistoryReadResult({
1630
1657
  statusCode: StatusCodes.BadHistoryOperationUnsupported
1631
1658
  });
1632
1659
  callback(null, result);
1633
1660
  }
1634
- const result = new HistoryReadResult({
1635
- statusCode: StatusCodes.BadHistoryOperationUnsupported
1636
- });
1637
- callback(null, result);
1638
- }
1639
-
1640
- public _historyPush(newDataValue: DataValue): any {
1641
- throw new Error("");
1642
- }
1643
-
1644
- public _historyReadRawAsync(
1645
- historyReadRawModifiedDetails: ReadRawModifiedDetails,
1646
- maxNumberToExtract: number,
1647
- isReversed: boolean,
1648
- reverseDataValue: boolean,
1649
- callback: CallbackT<DataValue[]>
1650
- ): any {
1651
- throw new Error("");
1652
- }
1653
-
1654
- public _historyReadModify(
1655
- context: ISessionContext,
1656
- historyReadRawModifiedDetails: ReadRawModifiedDetails,
1657
- indexRange: NumericRange | null,
1658
- dataEncoding: QualifiedNameLike | null,
1659
- continuationData: ContinuationData,
1660
- callback: CallbackT<HistoryReadResult>
1661
- ): any {
1662
- throw new Error("");
1663
- }
1664
1661
 
1665
- public _update_startOfOnlineArchive(newDate: Date): void {
1666
- // please install
1667
- throw new Error("");
1668
- }
1669
-
1670
- public _update_startOfArchive(newDate: Date): void {
1671
- throw new Error("");
1672
- }
1673
-
1674
- public _validate_DataType(variantDataType: DataType): boolean {
1675
- return validateDataType(this.addressSpace, this.dataType, variantDataType, this.nodeId, /* allow Nulls */ false);
1676
- }
1662
+ public _historyPush(newDataValue: DataValue): any {
1663
+ throw new Error("");
1664
+ }
1677
1665
 
1678
- public _internal_set_value(value: Variant): void {
1679
- if (value.dataType !== DataType.Null) {
1680
- this.verifyVariantCompatibility(value);
1666
+ public _historyReadRawAsync(
1667
+ historyReadRawModifiedDetails: ReadRawModifiedDetails,
1668
+ maxNumberToExtract: number,
1669
+ isReversed: boolean,
1670
+ reverseDataValue: boolean,
1671
+ callback: CallbackT<DataValue[]>
1672
+ ): any {
1673
+ throw new Error("");
1681
1674
  }
1682
- this.$dataValue.value = value;
1683
- }
1684
1675
 
1685
- public _internal_set_dataValue(dataValue: DataValue, indexRange?: NumericRange | null): void {
1686
- assert(dataValue, "expecting a dataValue");
1687
- assert(dataValue instanceof DataValue, "expecting dataValue to be a DataValue");
1688
- assert(dataValue !== this.$dataValue, "expecting dataValue to be different from previous DataValue instance");
1676
+ public _historyReadModify(
1677
+ context: ISessionContext,
1678
+ historyReadRawModifiedDetails: ReadRawModifiedDetails,
1679
+ indexRange: NumericRange | null,
1680
+ dataEncoding: QualifiedNameLike | null,
1681
+ continuationData: ContinuationData,
1682
+ callback: CallbackT<HistoryReadResult>
1683
+ ): any {
1684
+ throw new Error("");
1685
+ }
1689
1686
 
1690
- const addressSpace = this.addressSpace;
1687
+ public _update_startOfOnlineArchive(newDate: Date): void {
1688
+ // please install
1689
+ throw new Error("");
1690
+ }
1691
1691
 
1692
- // istanbul ignore next
1693
- if (!addressSpace) {
1694
- warningLog("UAVariable#_internal_set_dataValue : no addressSpace ! may be node has already been deleted ?");
1695
- return;
1692
+ public _update_startOfArchive(newDate: Date): void {
1693
+ throw new Error("");
1696
1694
  }
1697
1695
 
1698
- // istanbul ignore next
1699
- if (dataValue.value.arrayType === VariantArrayType.Matrix) {
1700
- if (!dataValue.value.dimensions) {
1701
- throw new Error("missing dimensions: a Matrix Variant needs a dimension");
1702
- }
1703
- const nbElements = dataValue.value.dimensions.reduce((acc, x) => acc * x, 1);
1704
- if (dataValue.value.value.length !== 0 && dataValue.value.value.length !== nbElements) {
1705
- throw new Error(
1706
- `Internal Error: matrix dimension doesn't match the number of element in the array : ${dataValue.toString()} "\n expecting ${nbElements} elements but got ${dataValue.value.value.length
1707
- }`
1708
- );
1709
- }
1696
+ public _validate_DataType(variantDataType: DataType): boolean {
1697
+ return validateDataType(this.addressSpace, this.dataType, variantDataType, this.nodeId, /* allow Nulls */ false);
1710
1698
  }
1711
1699
 
1712
- // istanbul ignore next
1713
- if (dataValue.value.dataType === DataType.ExtensionObject) {
1714
- // istanbul ignore next
1715
- if (!this.checkExtensionObjectIsCorrect(dataValue.value.value)) {
1716
- warningLog(dataValue.toString());
1717
- throw new Error("Invalid Extension Object on nodeId =" + this.nodeId.toString());
1700
+ public _internal_set_value(value: Variant): void {
1701
+ if (value.dataType !== DataType.Null) {
1702
+ this.verifyVariantCompatibility(value);
1718
1703
  }
1704
+ this.$dataValue.value = value;
1719
1705
  }
1720
1706
 
1721
- this.verifyVariantCompatibility(dataValue.value);
1707
+ public _internal_set_dataValue(dataValue: DataValue, indexRange?: NumericRange | null): void {
1708
+ assert(dataValue, "expecting a dataValue");
1709
+ assert(dataValue instanceof DataValue, "expecting dataValue to be a DataValue");
1710
+ assert(dataValue !== this.$dataValue, "expecting dataValue to be different from previous DataValue instance");
1722
1711
 
1723
- this._inner_replace_dataValue(dataValue, indexRange);
1724
- }
1712
+ const addressSpace = this.addressSpace;
1725
1713
 
1726
- /**
1727
- * @private
1728
- */
1729
- public _inner_replace_dataValue(dataValue: DataValue, indexRange?: NumericRange | null) {
1714
+ // istanbul ignore next
1715
+ if (!addressSpace) {
1716
+ warningLog("UAVariable#_internal_set_dataValue : no addressSpace ! may be node has already been deleted ?");
1717
+ return;
1718
+ }
1730
1719
 
1731
- assert(this.$dataValue.value instanceof Variant);
1732
- const old_dataValue = this.$dataValue.clone();
1720
+ // istanbul ignore next
1721
+ if (dataValue.value.arrayType === VariantArrayType.Matrix) {
1722
+ if (!dataValue.value.dimensions) {
1723
+ throw new Error("missing dimensions: a Matrix Variant needs a dimension");
1724
+ }
1725
+ const nbElements = dataValue.value.dimensions.reduce((acc, x) => acc * x, 1);
1726
+ if (dataValue.value.value.length !== 0 && dataValue.value.value.length !== nbElements) {
1727
+ throw new Error(
1728
+ `Internal Error: matrix dimension doesn't match the number of element in the array : ${dataValue.toString()} "\n expecting ${nbElements} elements but got ${dataValue.value.value.length
1729
+ }`
1730
+ );
1731
+ }
1732
+ }
1733
1733
 
1734
- if (this.$$extensionObjectArray && dataValue.value.arrayType !== VariantArrayType.Scalar) {
1735
- // we have a bounded array or matrix
1736
- assert(Array.isArray(dataValue.value.value));
1737
- if (this.$$extensionObjectArray !== this.$dataValue.value.value) {
1738
- throw new Error("internal error");
1734
+ // istanbul ignore next
1735
+ if (dataValue.value.dataType === DataType.ExtensionObject) {
1736
+ // istanbul ignore next
1737
+ if (!this.checkExtensionObjectIsCorrect(dataValue.value.value)) {
1738
+ warningLog(dataValue.toString());
1739
+ throw new Error("Invalid Extension Object on nodeId =" + this.nodeId.toString());
1740
+ }
1739
1741
  }
1740
- this.$$extensionObjectArray = dataValue.value.value;
1741
- this.$dataValue.value.value = dataValue.value.value;
1742
1742
 
1743
- this.$dataValue.statusCode = dataValue.statusCode || StatusCodes.Good;
1744
- this.$dataValue.serverTimestamp = dataValue.serverTimestamp;
1745
- this.$dataValue.serverPicoseconds = dataValue.serverPicoseconds;
1746
- this.$dataValue.sourceTimestamp = dataValue.sourceTimestamp;
1747
- this.$dataValue.sourcePicoseconds = dataValue.sourcePicoseconds;
1743
+ this.verifyVariantCompatibility(dataValue.value);
1748
1744
 
1749
- } else {
1750
- this.$dataValue = dataValue;
1751
- this.$dataValue.statusCode = this.$dataValue.statusCode || StatusCodes.Good;
1752
- }
1753
- // repair missing timestamps
1754
- const now = new Date();
1755
- if (!dataValue.serverTimestamp) {
1756
- this.$dataValue.serverTimestamp = old_dataValue.serverTimestamp || now;
1757
- this.$dataValue.serverPicoseconds = old_dataValue.serverPicoseconds || 0;
1758
- }
1759
- if (!dataValue.sourceTimestamp) {
1760
- this.$dataValue.sourceTimestamp = old_dataValue.sourceTimestamp || now;
1761
- this.$dataValue.sourcePicoseconds = old_dataValue.sourcePicoseconds || 0;
1745
+ this._inner_replace_dataValue(dataValue, indexRange);
1762
1746
  }
1763
1747
 
1764
- if (!sameDataValue(old_dataValue, dataValue)) {
1765
- if (this.getBasicDataType() === DataType.ExtensionObject) {
1748
+ /**
1749
+ * @private
1750
+ */
1751
+ public _inner_replace_dataValue(dataValue: DataValue, indexRange?: NumericRange | null) {
1752
+
1753
+ assert(this.$dataValue.value instanceof Variant);
1754
+ const old_dataValue = this.$dataValue.clone();
1755
+
1756
+ if (this.$$extensionObjectArray && dataValue.value.arrayType !== VariantArrayType.Scalar) {
1757
+ // we have a bounded array or matrix
1758
+ assert(Array.isArray(dataValue.value.value));
1759
+ if (this.$$extensionObjectArray !== this.$dataValue.value.value) {
1760
+ throw new Error("internal error");
1761
+ }
1762
+ this.$$extensionObjectArray = dataValue.value.value;
1763
+ this.$dataValue.value.value = dataValue.value.value;
1764
+
1765
+ this.$dataValue.statusCode = dataValue.statusCode || StatusCodes.Good;
1766
+ this.$dataValue.serverTimestamp = dataValue.serverTimestamp;
1767
+ this.$dataValue.serverPicoseconds = dataValue.serverPicoseconds;
1768
+ this.$dataValue.sourceTimestamp = dataValue.sourceTimestamp;
1769
+ this.$dataValue.sourcePicoseconds = dataValue.sourcePicoseconds;
1770
+
1771
+ } else if (this._basicDataType === DataType.ExtensionObject && this.valueRank === -1 && this.$set_ExtensionObject && dataValue.value.arrayType === VariantArrayType.Scalar) {
1772
+ // the entire extension object is changed.
1773
+ this.$dataValue.statusCode = this.$dataValue.statusCode || StatusCodes.Good;
1766
1774
  const preciseClock = coerceClock(this.$dataValue.sourceTimestamp, this.$dataValue.sourcePicoseconds);
1767
- const cache: Set<UAVariable> = new Set();
1768
- if (this.$$extensionObjectArray) {
1769
- this.touchValue(preciseClock);
1770
- propagateTouchValueDownwardArray(this, preciseClock, cache);
1775
+ this.$set_ExtensionObject(dataValue.value.value, preciseClock, new Set())
1776
+ } else {
1777
+ this.$dataValue = dataValue;
1778
+ this.$dataValue.statusCode = this.$dataValue.statusCode || StatusCodes.Good;
1779
+ }
1780
+ // repair missing timestamps
1781
+ const now = new Date();
1782
+ if (!dataValue.serverTimestamp) {
1783
+ this.$dataValue.serverTimestamp = old_dataValue.serverTimestamp || now;
1784
+ this.$dataValue.serverPicoseconds = old_dataValue.serverPicoseconds || 0;
1785
+ }
1786
+ if (!dataValue.sourceTimestamp) {
1787
+ this.$dataValue.sourceTimestamp = old_dataValue.sourceTimestamp || now;
1788
+ this.$dataValue.sourcePicoseconds = old_dataValue.sourcePicoseconds || 0;
1789
+ }
1790
+
1791
+ if (!sameDataValue(old_dataValue, dataValue)) {
1792
+ if (this.getBasicDataType() === DataType.ExtensionObject) {
1793
+ const preciseClock = coerceClock(this.$dataValue.sourceTimestamp, this.$dataValue.sourcePicoseconds);
1794
+ const cache: Set<UAVariable> = new Set();
1795
+ if (this.$$extensionObjectArray) {
1796
+ this.touchValue(preciseClock);
1797
+ propagateTouchValueDownwardArray(this, preciseClock, cache);
1798
+ } else {
1799
+ this.touchValue(preciseClock);
1800
+ propagateTouchValueDownward(this, preciseClock, cache);
1801
+ }
1771
1802
  } else {
1772
- this.touchValue(preciseClock);
1773
- propagateTouchValueDownward(this, preciseClock, cache);
1803
+ this.emit("value_changed", this.$dataValue, indexRange);
1774
1804
  }
1775
- } else {
1776
- this.emit("value_changed", this.$dataValue, indexRange);
1777
1805
  }
1778
1806
  }
1779
- }
1780
1807
 
1781
- public _conditionRefresh(_cache?: ConditionRefreshCache): void {
1782
- apply_condition_refresh.call(this, _cache);
1783
- }
1808
+ public _conditionRefresh(_cache?: ConditionRefreshCache): void {
1809
+ apply_condition_refresh.call(this, _cache);
1810
+ }
1784
1811
 
1785
- public handle_semantic_changed(): void {
1786
- this.semantic_version = this.semantic_version + 1;
1787
- this.emit("semantic_changed");
1788
- }
1812
+ public handle_semantic_changed(): void {
1813
+ this.semantic_version = this.semantic_version + 1;
1814
+ this.emit("semantic_changed");
1815
+ }
1789
1816
 
1790
- private _readDataType(): DataValue {
1791
- assert(this.dataType instanceof NodeId);
1792
- const options = {
1793
- statusCode: StatusCodes.Good,
1794
- value: {
1795
- dataType: DataType.NodeId,
1796
- value: this.dataType
1797
- }
1798
- };
1799
- return new DataValue(options);
1800
- }
1817
+ private _readDataType(): DataValue {
1818
+ assert(this.dataType instanceof NodeId);
1819
+ const options = {
1820
+ statusCode: StatusCodes.Good,
1821
+ value: {
1822
+ dataType: DataType.NodeId,
1823
+ value: this.dataType
1824
+ }
1825
+ };
1826
+ return new DataValue(options);
1827
+ }
1801
1828
 
1802
- private _readValueRank(): DataValue {
1803
- assert(typeof this.valueRank === "number");
1804
- const options = {
1805
- statusCode: StatusCodes.Good,
1806
- value: { dataType: DataType.Int32, value: this.valueRank }
1807
- };
1808
- return new DataValue(options);
1809
- }
1829
+ private _readValueRank(): DataValue {
1830
+ assert(typeof this.valueRank === "number");
1831
+ const options = {
1832
+ statusCode: StatusCodes.Good,
1833
+ value: { dataType: DataType.Int32, value: this.valueRank }
1834
+ };
1835
+ return new DataValue(options);
1836
+ }
1810
1837
 
1811
- private _readArrayDimensions(): DataValue {
1812
- assert(Array.isArray(this.arrayDimensions) || this.arrayDimensions === null);
1813
- assert(!this.arrayDimensions || this.valueRank > 0, "arrayDimension must be null if valueRank <0");
1814
- const options = {
1815
- statusCode: StatusCodes.Good,
1816
- value: { dataType: DataType.UInt32, arrayType: VariantArrayType.Array, value: this.arrayDimensions }
1817
- };
1818
- return new DataValue(options);
1819
- }
1838
+ private _readArrayDimensions(): DataValue {
1839
+ assert(Array.isArray(this.arrayDimensions) || this.arrayDimensions === null);
1840
+ assert(!this.arrayDimensions || this.valueRank > 0, "arrayDimension must be null if valueRank <0");
1841
+ const options = {
1842
+ statusCode: StatusCodes.Good,
1843
+ value: { dataType: DataType.UInt32, arrayType: VariantArrayType.Array, value: this.arrayDimensions }
1844
+ };
1845
+ return new DataValue(options);
1846
+ }
1820
1847
 
1821
- private _readAccessLevel(context: ISessionContext): DataValue {
1822
- assert(context instanceof SessionContext);
1823
- const options = {
1824
- statusCode: StatusCodes.Good,
1825
- value: { dataType: DataType.Byte, value: convertAccessLevelFlagToByte(this.accessLevel) }
1826
- };
1827
- return new DataValue(options);
1828
- }
1848
+ private _readAccessLevel(context: ISessionContext): DataValue {
1849
+ assert(context instanceof SessionContext);
1850
+ const options = {
1851
+ statusCode: StatusCodes.Good,
1852
+ value: { dataType: DataType.Byte, value: convertAccessLevelFlagToByte(this.accessLevel) }
1853
+ };
1854
+ return new DataValue(options);
1855
+ }
1829
1856
 
1830
- private _readAccessLevelEx(context: ISessionContext): DataValue {
1831
- assert(context instanceof SessionContext);
1832
- const options = {
1833
- statusCode: StatusCodes.Good,
1834
- // Extra flags are not supported yet. to do:
1835
- value: { dataType: DataType.UInt32, value: convertAccessLevelFlagToByte(this.accessLevel) }
1836
- };
1837
- return new DataValue(options);
1838
- }
1857
+ private _readAccessLevelEx(context: ISessionContext): DataValue {
1858
+ assert(context instanceof SessionContext);
1859
+ const options = {
1860
+ statusCode: StatusCodes.Good,
1861
+ // Extra flags are not supported yet. to do:
1862
+ value: { dataType: DataType.UInt32, value: convertAccessLevelFlagToByte(this.accessLevel) }
1863
+ };
1864
+ return new DataValue(options);
1865
+ }
1839
1866
 
1840
- private _readUserAccessLevel(context: ISessionContext): DataValue {
1841
- assert(context instanceof SessionContext);
1867
+ private _readUserAccessLevel(context: ISessionContext): DataValue {
1868
+ assert(context instanceof SessionContext);
1842
1869
 
1843
- const effectiveUserAccessLevel = _calculateEffectiveUserAccessLevelFromPermission(this, context, this.userAccessLevel);
1870
+ const effectiveUserAccessLevel = _calculateEffectiveUserAccessLevelFromPermission(this, context, this.userAccessLevel);
1844
1871
 
1845
- const options = {
1846
- value: {
1847
- dataType: DataType.Byte,
1848
- statusCode: StatusCodes.Good,
1849
- value: convertAccessLevelFlagToByte(effectiveUserAccessLevel)
1872
+ const options = {
1873
+ value: {
1874
+ dataType: DataType.Byte,
1875
+ statusCode: StatusCodes.Good,
1876
+ value: convertAccessLevelFlagToByte(effectiveUserAccessLevel)
1877
+ }
1878
+ };
1879
+ return new DataValue(options);
1880
+ }
1881
+
1882
+ private _readMinimumSamplingInterval(): DataValue {
1883
+ // expect a Duration => Double
1884
+ const options: DataValueLike = {};
1885
+ if (this.minimumSamplingInterval === undefined) {
1886
+ options.statusCode = StatusCodes.BadAttributeIdInvalid;
1887
+ } else {
1888
+ options.value = { dataType: DataType.Double, value: this.minimumSamplingInterval };
1889
+ options.statusCode = StatusCodes.Good;
1850
1890
  }
1851
- };
1852
- return new DataValue(options);
1853
- }
1891
+ return new DataValue(options);
1892
+ }
1854
1893
 
1855
- private _readMinimumSamplingInterval(): DataValue {
1856
- // expect a Duration => Double
1857
- const options: DataValueLike = {};
1858
- if (this.minimumSamplingInterval === undefined) {
1859
- options.statusCode = StatusCodes.BadAttributeIdInvalid;
1860
- } else {
1861
- options.value = { dataType: DataType.Double, value: this.minimumSamplingInterval };
1862
- options.statusCode = StatusCodes.Good;
1894
+ private _readHistorizing(): DataValue {
1895
+ assert(typeof this.historizing === "boolean");
1896
+ const options = {
1897
+ statusCode: StatusCodes.Good,
1898
+ value: { dataType: DataType.Boolean, value: !!this.historizing }
1899
+ };
1900
+ return new DataValue(options);
1863
1901
  }
1864
- return new DataValue(options);
1865
1902
  }
1866
1903
 
1867
- private _readHistorizing(): DataValue {
1868
- assert(typeof this.historizing === "boolean");
1869
- const options = {
1870
- statusCode: StatusCodes.Good,
1871
- value: { dataType: DataType.Boolean, value: !!this.historizing }
1872
- };
1873
- return new DataValue(options);
1904
+ // tslint:disable:no-var-requires
1905
+ const thenify = require("thenify");
1906
+ UAVariableImpl.prototype.asyncRefresh = thenify.withCallback(UAVariableImpl.prototype.asyncRefresh);
1907
+ UAVariableImpl.prototype.writeValue = thenify.withCallback(UAVariableImpl.prototype.writeValue);
1908
+ UAVariableImpl.prototype.writeAttribute = thenify.withCallback(UAVariableImpl.prototype.writeAttribute);
1909
+ UAVariableImpl.prototype.historyRead = thenify.withCallback(UAVariableImpl.prototype.historyRead);
1910
+ UAVariableImpl.prototype.readValueAsync = thenify.withCallback(UAVariableImpl.prototype.readValueAsync);
1911
+
1912
+ export interface UAVariableImplExtArray {
1913
+ $$variableType?: UAVariableType;
1914
+ $$dataType: UADataType;
1915
+ $$getElementBrowseName: (extObject: ExtensionObject, index: number | number[]) => QualifiedName;
1916
+ $$extensionObjectArray: ExtensionObject[];
1917
+ $$indexPropertyName: string;
1874
1918
  }
1875
- }
1876
-
1877
- // tslint:disable:no-var-requires
1878
- const thenify = require("thenify");
1879
- UAVariableImpl.prototype.asyncRefresh = thenify.withCallback(UAVariableImpl.prototype.asyncRefresh);
1880
- UAVariableImpl.prototype.writeValue = thenify.withCallback(UAVariableImpl.prototype.writeValue);
1881
- UAVariableImpl.prototype.writeAttribute = thenify.withCallback(UAVariableImpl.prototype.writeAttribute);
1882
- UAVariableImpl.prototype.historyRead = thenify.withCallback(UAVariableImpl.prototype.historyRead);
1883
- UAVariableImpl.prototype.readValueAsync = thenify.withCallback(UAVariableImpl.prototype.readValueAsync);
1884
-
1885
- export interface UAVariableImplExtArray {
1886
- $$variableType?: UAVariableType;
1887
- $$dataType: UADataType;
1888
- $$getElementBrowseName: (extObject: ExtensionObject, index: number | number[]) => QualifiedName;
1889
- $$extensionObjectArray: ExtensionObject[];
1890
- $$indexPropertyName: string;
1891
- }
1892
- export interface UAVariableImpl extends UAVariableImplExtArray {
1893
- }
1894
- function check_valid_array(dataType: DataType, array: any): boolean {
1895
- if (Array.isArray(array)) {
1896
- return true;
1919
+ export interface UAVariableImpl extends UAVariableImplExtArray {
1897
1920
  }
1898
- switch (dataType) {
1899
- case DataType.Double:
1900
- return array instanceof Float64Array;
1901
- case DataType.Float:
1902
- return array instanceof Float32Array;
1903
- case DataType.Int32:
1904
- return array instanceof Int32Array;
1905
- case DataType.Int16:
1906
- return array instanceof Int16Array;
1907
- case DataType.SByte:
1908
- return array instanceof Int8Array;
1909
- case DataType.UInt32:
1910
- return array instanceof Uint32Array;
1911
- case DataType.UInt16:
1912
- return array instanceof Uint16Array;
1913
- case DataType.Byte:
1914
- return array instanceof Uint8Array || array instanceof Buffer;
1921
+ function check_valid_array(dataType: DataType, array: any): boolean {
1922
+ if (Array.isArray(array)) {
1923
+ return true;
1924
+ }
1925
+ switch (dataType) {
1926
+ case DataType.Double:
1927
+ return array instanceof Float64Array;
1928
+ case DataType.Float:
1929
+ return array instanceof Float32Array;
1930
+ case DataType.Int32:
1931
+ return array instanceof Int32Array;
1932
+ case DataType.Int16:
1933
+ return array instanceof Int16Array;
1934
+ case DataType.SByte:
1935
+ return array instanceof Int8Array;
1936
+ case DataType.UInt32:
1937
+ return array instanceof Uint32Array;
1938
+ case DataType.UInt16:
1939
+ return array instanceof Uint16Array;
1940
+ case DataType.Byte:
1941
+ return array instanceof Uint8Array || array instanceof Buffer;
1942
+ }
1943
+ return false;
1915
1944
  }
1916
- return false;
1917
- }
1918
1945
 
1919
- function _apply_default_timestamps(dataValue: DataValue): void {
1920
- const now = getCurrentClock();
1921
- assert(dataValue instanceof DataValue);
1946
+ function _apply_default_timestamps(dataValue: DataValue): void {
1947
+ const now = getCurrentClock();
1948
+ assert(dataValue instanceof DataValue);
1949
+
1950
+ if (!dataValue.sourceTimestamp) {
1951
+ dataValue.sourceTimestamp = now.timestamp;
1952
+ dataValue.sourcePicoseconds = now.picoseconds;
1953
+ }
1954
+ if (!dataValue.serverTimestamp) {
1955
+ dataValue.serverTimestamp = now.timestamp;
1956
+ dataValue.serverPicoseconds = now.picoseconds;
1957
+ }
1958
+ }
1922
1959
 
1923
- if (!dataValue.sourceTimestamp) {
1924
- dataValue.sourceTimestamp = now.timestamp;
1925
- dataValue.sourcePicoseconds = now.picoseconds;
1960
+ function unsetFlag(flags: number, mask: number): number {
1961
+ return flags & ~mask;
1926
1962
  }
1927
- if (!dataValue.serverTimestamp) {
1928
- dataValue.serverTimestamp = now.timestamp;
1929
- dataValue.serverPicoseconds = now.picoseconds;
1963
+ function setFlag(flags: number, mask: number): number {
1964
+ return flags | mask;
1930
1965
  }
1931
- }
1932
-
1933
- function unsetFlag(flags: number, mask: number): number {
1934
- return flags & ~mask;
1935
- }
1936
- function setFlag(flags: number, mask: number): number {
1937
- return flags | mask;
1938
- }
1939
-
1940
- function _calculateEffectiveUserAccessLevelFromPermission(
1941
- node: UAVariable,
1942
- context: ISessionContext,
1943
- userAccessLevel: AccessLevelFlag | undefined
1944
- ): AccessLevelFlag {
1945
- function __adjustFlag(
1946
- permissionType: PermissionType,
1947
- access: AccessLevelFlag,
1948
- userAccessLevel1: AccessLevelFlag
1966
+
1967
+ function _calculateEffectiveUserAccessLevelFromPermission(
1968
+ node: UAVariable,
1969
+ context: ISessionContext,
1970
+ userAccessLevel: AccessLevelFlag | undefined
1949
1971
  ): AccessLevelFlag {
1950
- if ((node.accessLevel & access) === 0 || (userAccessLevel1 & access) === 0) {
1951
- userAccessLevel1 = unsetFlag(userAccessLevel1, access);
1952
- } else {
1953
- if (!context.checkPermission(node, permissionType)) {
1972
+ function __adjustFlag(
1973
+ permissionType: PermissionType,
1974
+ access: AccessLevelFlag,
1975
+ userAccessLevel1: AccessLevelFlag
1976
+ ): AccessLevelFlag {
1977
+ if ((node.accessLevel & access) === 0 || (userAccessLevel1 & access) === 0) {
1954
1978
  userAccessLevel1 = unsetFlag(userAccessLevel1, access);
1979
+ } else {
1980
+ if (!context.checkPermission(node, permissionType)) {
1981
+ userAccessLevel1 = unsetFlag(userAccessLevel1, access);
1982
+ }
1955
1983
  }
1984
+ return userAccessLevel1;
1985
+ }
1986
+ userAccessLevel = node.userAccessLevel === undefined ? node.accessLevel : node.userAccessLevel & node.accessLevel;
1987
+ if (context.checkPermission) {
1988
+ assert(context.checkPermission instanceof Function);
1989
+ userAccessLevel = __adjustFlag(PermissionType.Read, AccessLevelFlag.CurrentRead, userAccessLevel);
1990
+ userAccessLevel = __adjustFlag(PermissionType.Write, AccessLevelFlag.CurrentWrite, userAccessLevel);
1991
+ userAccessLevel = __adjustFlag(PermissionType.Write, AccessLevelFlag.StatusWrite, userAccessLevel);
1992
+ userAccessLevel = __adjustFlag(PermissionType.Write, AccessLevelFlag.TimestampWrite, userAccessLevel);
1993
+ userAccessLevel = __adjustFlag(PermissionType.ReadHistory, AccessLevelFlag.HistoryRead, userAccessLevel);
1994
+ userAccessLevel = __adjustFlag(PermissionType.DeleteHistory, AccessLevelFlag.HistoryWrite, userAccessLevel);
1995
+ return userAccessLevel;
1996
+ } else {
1997
+ return userAccessLevel;
1956
1998
  }
1957
- return userAccessLevel1;
1958
1999
  }
1959
- userAccessLevel = node.userAccessLevel === undefined ? node.accessLevel : node.userAccessLevel & node.accessLevel;
1960
- if (context.checkPermission) {
1961
- assert(context.checkPermission instanceof Function);
1962
- userAccessLevel = __adjustFlag(PermissionType.Read, AccessLevelFlag.CurrentRead, userAccessLevel);
1963
- userAccessLevel = __adjustFlag(PermissionType.Write, AccessLevelFlag.CurrentWrite, userAccessLevel);
1964
- userAccessLevel = __adjustFlag(PermissionType.Write, AccessLevelFlag.StatusWrite, userAccessLevel);
1965
- userAccessLevel = __adjustFlag(PermissionType.Write, AccessLevelFlag.TimestampWrite, userAccessLevel);
1966
- userAccessLevel = __adjustFlag(PermissionType.ReadHistory, AccessLevelFlag.HistoryRead, userAccessLevel);
1967
- userAccessLevel = __adjustFlag(PermissionType.DeleteHistory, AccessLevelFlag.HistoryWrite, userAccessLevel);
1968
- return userAccessLevel;
1969
- } else {
1970
- return userAccessLevel;
2000
+
2001
+ function adjustVariant2(this: UAVariableImpl, variant: Variant): Variant {
2002
+ // convert Variant( Scalar|ByteString) => Variant(Array|ByteArray)
2003
+ const addressSpace = this.addressSpace;
2004
+ const basicType = this.getBasicDataType();
2005
+ variant = adjustVariant(variant, this.valueRank, basicType);
2006
+ return variant;
1971
2007
  }
1972
- }
1973
-
1974
- function adjustVariant2(this: UAVariableImpl, variant: Variant): Variant {
1975
- // convert Variant( Scalar|ByteString) => Variant(Array|ByteArray)
1976
- const addressSpace = this.addressSpace;
1977
- const basicType = this.getBasicDataType();
1978
- variant = adjustVariant(variant, this.valueRank, basicType);
1979
- return variant;
1980
- }
1981
-
1982
- function _not_writable_timestamped_set_func(
1983
- dataValue: DataValue,
1984
- callback: (err: Error | null, statusCode: StatusCode, dataValue?: DataValue | null) => void
1985
- ) {
1986
- assert(dataValue instanceof DataValue);
1987
- callback(null, StatusCodes.BadNotWritable, null);
1988
- }
1989
-
1990
- function _default_writable_timestamped_set_func(
1991
- dataValue: DataValue,
1992
- callback: (err: Error | null, statusCode: StatusCode, dataValue?: DataValue | null) => void
1993
- ) {
1994
- assert(dataValue instanceof DataValue);
1995
- callback(null, StatusCodes.Good, dataValue);
1996
- }
1997
-
1998
- function turn_sync_to_async<T, D, R>(f: (this: T, data: D) => R, numberOfArgs: number) {
1999
- if (f.length <= numberOfArgs) {
2000
- return function (this: T, data: D, callback: (err: Error | null, r?: R) => void) {
2001
- const r = f.call(this, data);
2002
- setImmediate(() => {
2003
- return callback(null, r);
2004
- });
2005
- };
2006
- } else {
2007
- assert(f.length === numberOfArgs + 1);
2008
- return f;
2008
+
2009
+ function _not_writable_timestamped_set_func(
2010
+ dataValue: DataValue,
2011
+ callback: (err: Error | null, statusCode: StatusCode, dataValue?: DataValue | null) => void
2012
+ ) {
2013
+ assert(dataValue instanceof DataValue);
2014
+ callback(null, StatusCodes.BadNotWritable, null);
2009
2015
  }
2010
- }
2011
2016
 
2012
- const _default_minimumSamplingInterval = 1000;
2017
+ function _default_writable_timestamped_set_func(
2018
+ dataValue: DataValue,
2019
+ callback: (err: Error | null, statusCode: StatusCode, dataValue?: DataValue | null) => void
2020
+ ) {
2021
+ assert(dataValue instanceof DataValue);
2022
+ callback(null, StatusCodes.Good, dataValue);
2023
+ }
2013
2024
 
2014
- function coerceDataValue(dataValue: DataValue | DataValueLike): DataValue {
2015
- if (dataValue instanceof DataValue) {
2016
- return dataValue;
2025
+ function turn_sync_to_async<T, D, R>(f: (this: T, data: D) => R, numberOfArgs: number) {
2026
+ if (f.length <= numberOfArgs) {
2027
+ return function (this: T, data: D, callback: (err: Error | null, r?: R) => void) {
2028
+ const r = f.call(this, data);
2029
+ setImmediate(() => {
2030
+ return callback(null, r);
2031
+ });
2032
+ };
2033
+ } else {
2034
+ assert(f.length === numberOfArgs + 1);
2035
+ return f;
2036
+ }
2017
2037
  }
2018
- return new DataValue(dataValue);
2019
- }
2020
2038
 
2021
- // variation #3 :
2022
- function _Variable_bind_with_async_refresh(this: UAVariableImpl, options: any) {
2023
- /* jshint validthis: true */
2024
- assert(this instanceof UAVariableImpl);
2039
+ const _default_minimumSamplingInterval = 1000;
2025
2040
 
2026
- assert(typeof options.refreshFunc === "function");
2027
- assert(!options.get, "a getter shall not be specified when refreshFunc is set");
2028
- assert(!options.timestamped_get, "a getter shall not be specified when refreshFunc is set");
2041
+ function coerceDataValue(dataValue: DataValue | DataValueLike): DataValue {
2042
+ if (dataValue instanceof DataValue) {
2043
+ return dataValue;
2044
+ }
2045
+ return new DataValue(dataValue);
2046
+ }
2029
2047
 
2030
- assert(!this.refreshFunc);
2048
+ // variation #3 :
2049
+ function _Variable_bind_with_async_refresh(this: UAVariableImpl, options: any) {
2050
+ /* jshint validthis: true */
2051
+ assert(this instanceof UAVariableImpl);
2031
2052
 
2032
- this.refreshFunc = options.refreshFunc;
2053
+ assert(typeof options.refreshFunc === "function");
2054
+ assert(!options.get, "a getter shall not be specified when refreshFunc is set");
2055
+ assert(!options.timestamped_get, "a getter shall not be specified when refreshFunc is set");
2033
2056
 
2034
- // TO DO : REVISIT THIS ASSUMPTION
2035
- if (false && this.minimumSamplingInterval === 0) {
2036
- // when a getter /timestamped_getter or async_getter is provided
2037
- // samplingInterval cannot be 0, as the item value must be scanned to be updated.
2038
- this.minimumSamplingInterval = _default_minimumSamplingInterval; // MonitoredItem.minimumSamplingInterval;
2039
- debugLog("adapting minimumSamplingInterval on " + this.browseName.toString() + " to " + this.minimumSamplingInterval);
2040
- }
2041
- }
2042
-
2043
- // variation 2
2044
- function _Variable_bind_with_timestamped_get(
2045
- this: UAVariableImpl,
2046
- options: {
2047
- get: undefined;
2048
- timestamped_get: TimestampGetFunc;
2049
- }
2050
- ) {
2051
- /* jshint validthis: true */
2052
- assert(this instanceof UAVariableImpl);
2053
- assert(typeof options.timestamped_get === "function");
2054
- assert(!options.get, "should not specify 'get' when 'timestamped_get' exists ");
2055
- assert(!this._timestamped_get_func);
2056
-
2057
- const async_refresh_func = (callback: (err: Error | null, dataValue?: DataValue) => void) => {
2058
- Promise.resolve((this._timestamped_get_func! as VariableDataValueGetterSync).call(this))
2059
- .then((dataValue) => callback(null, dataValue))
2060
- .catch((err) => {
2061
- errorLog("asyncRefresh error: Variable is ", this.nodeId.toString(), this.browseName.toString());
2062
- callback(err as Error);
2063
- });
2064
- };
2065
- const pThis = this as UAVariable;
2066
- if (options.timestamped_get.length === 0) {
2067
- const timestamped_get = options.timestamped_get as (this: UAVariable) => DataValue | Promise<DataValue>;
2068
- // sync version | Promise version
2069
- this._timestamped_get_func = timestamped_get;
2070
-
2071
- const dataValue_verify = timestamped_get.call(pThis);
2072
- // dataValue_verify should be a DataValue or a Promise
2073
- /* istanbul ignore next */
2074
- if (!(dataValue_verify instanceof DataValue) && typeof dataValue_verify.then !== "function") {
2075
- errorLog(
2076
- chalk.red(" Bind variable error: "),
2077
- " the timestamped_get function must return a DataValue or a Promise<DataValue>" +
2078
- "\n value_check.constructor.name ",
2079
- dataValue_verify ? dataValue_verify.constructor.name : "null"
2080
- );
2057
+ assert(!this.refreshFunc);
2058
+
2059
+ this.refreshFunc = options.refreshFunc;
2081
2060
 
2082
- throw new Error(" Bind variable error: " + " the timestamped_get function must return a DataValue");
2061
+ // TO DO : REVISIT THIS ASSUMPTION
2062
+ if (false && this.minimumSamplingInterval === 0) {
2063
+ // when a getter /timestamped_getter or async_getter is provided
2064
+ // samplingInterval cannot be 0, as the item value must be scanned to be updated.
2065
+ this.minimumSamplingInterval = _default_minimumSamplingInterval; // MonitoredItem.minimumSamplingInterval;
2066
+ debugLog("adapting minimumSamplingInterval on " + this.browseName.toString() + " to " + this.minimumSamplingInterval);
2083
2067
  }
2084
- _Variable_bind_with_async_refresh.call(this, { refreshFunc: async_refresh_func });
2085
- } else if (options.timestamped_get.length === 1) {
2086
- _Variable_bind_with_async_refresh.call(this, { refreshFunc: options.timestamped_get });
2087
- } else {
2088
- errorLog("timestamped_get has a invalid number of argument , should be 0 or 1 ");
2089
- throw new Error("timestamped_get has a invalid number of argument , should be 0 or 1 ");
2090
2068
  }
2091
- }
2092
-
2093
- // variation 1
2094
- function _Variable_bind_with_simple_get(this: UAVariableImpl, options: GetterOptions) {
2095
- /* jshint validthis: true */
2096
- assert(this instanceof UAVariableImpl);
2097
- assert(typeof options.get === "function", "should specify get function");
2098
- assert(options.get!.length === 0, "get function should not have arguments");
2099
- assert(!options.timestamped_get, "should not specify a timestamped_get function when get is specified");
2100
- assert(!this._timestamped_get_func);
2101
- assert(!this._get_func);
2102
2069
 
2103
- this._get_func = options.get;
2070
+ // variation 2
2071
+ function _Variable_bind_with_timestamped_get(
2072
+ this: UAVariableImpl,
2073
+ options: {
2074
+ get: undefined;
2075
+ timestamped_get: TimestampGetFunc;
2076
+ }
2077
+ ) {
2078
+ /* jshint validthis: true */
2079
+ assert(this instanceof UAVariableImpl);
2080
+ assert(typeof options.timestamped_get === "function");
2081
+ assert(!options.get, "should not specify 'get' when 'timestamped_get' exists ");
2082
+ assert(!this._timestamped_get_func);
2104
2083
 
2105
- const timestamped_get_func_from__Variable_bind_with_simple_get = () => {
2106
- const value: Variant | StatusCode = this._get_func();
2084
+ const async_refresh_func = (callback: (err: Error | null, dataValue?: DataValue) => void) => {
2085
+ Promise.resolve((this._timestamped_get_func! as VariableDataValueGetterSync).call(this))
2086
+ .then((dataValue) => callback(null, dataValue))
2087
+ .catch((err) => {
2088
+ errorLog("asyncRefresh error: Variable is ", this.nodeId.toString(), this.browseName.toString());
2089
+ callback(err as Error);
2090
+ });
2091
+ };
2092
+ const pThis = this as UAVariable;
2093
+ if (options.timestamped_get.length === 0) {
2094
+ const timestamped_get = options.timestamped_get as (this: UAVariable) => DataValue | Promise<DataValue>;
2095
+ // sync version | Promise version
2096
+ this._timestamped_get_func = timestamped_get;
2097
+
2098
+ const dataValue_verify = timestamped_get.call(pThis);
2099
+ // dataValue_verify should be a DataValue or a Promise
2100
+ /* istanbul ignore next */
2101
+ if (!(dataValue_verify instanceof DataValue) && typeof dataValue_verify.then !== "function") {
2102
+ errorLog(
2103
+ chalk.red(" Bind variable error: "),
2104
+ " the timestamped_get function must return a DataValue or a Promise<DataValue>" +
2105
+ "\n value_check.constructor.name ",
2106
+ dataValue_verify ? dataValue_verify.constructor.name : "null"
2107
+ );
2107
2108
 
2108
- /* istanbul ignore next */
2109
- if (!is_Variant_or_StatusCode(value)) {
2110
- errorLog(
2111
- chalk.red(" Bind variable error: "),
2112
- " : the getter must return a Variant or a StatusCode" + "\nvalue_check.constructor.name ",
2113
- value ? value.constructor.name : "null"
2114
- );
2115
- throw new Error(
2116
- " bindVariable : the value getter function returns a invalid result ( expecting a Variant or a StatusCode !!!"
2117
- );
2118
- }
2119
- if (is_StatusCode(value)) {
2120
- return new DataValue({ statusCode: value as StatusCode });
2121
- } else {
2122
- if (!this.$dataValue || !this.$dataValue.statusCode.isGoodish() || !sameVariant(this.$dataValue.value, value as Variant)) {
2123
- this._inner_replace_dataValue(new DataValue({ value }));
2109
+ throw new Error(" Bind variable error: " + " the timestamped_get function must return a DataValue");
2124
2110
  }
2125
- return this.$dataValue;
2111
+ _Variable_bind_with_async_refresh.call(this, { refreshFunc: async_refresh_func });
2112
+ } else if (options.timestamped_get.length === 1) {
2113
+ _Variable_bind_with_async_refresh.call(this, { refreshFunc: options.timestamped_get });
2114
+ } else {
2115
+ errorLog("timestamped_get has a invalid number of argument , should be 0 or 1 ");
2116
+ throw new Error("timestamped_get has a invalid number of argument , should be 0 or 1 ");
2126
2117
  }
2127
- };
2128
-
2129
- _Variable_bind_with_timestamped_get.call(this, {
2130
- get: undefined,
2131
- timestamped_get: timestamped_get_func_from__Variable_bind_with_simple_get
2132
- });
2133
- }
2118
+ }
2134
2119
 
2135
- function _Variable_bind_with_simple_set(this: UAVariableImpl, options: any) {
2136
- assert(this instanceof UAVariableImpl);
2137
- assert(typeof options.set === "function", "should specify set function");
2138
- assert(!options.timestamped_set, "should not specify a timestamped_set function");
2120
+ // variation 1
2121
+ function _Variable_bind_with_simple_get(this: UAVariableImpl, options: GetterOptions) {
2122
+ /* jshint validthis: true */
2123
+ assert(this instanceof UAVariableImpl);
2124
+ assert(typeof options.get === "function", "should specify get function");
2125
+ assert(options.get!.length === 0, "get function should not have arguments");
2126
+ assert(!options.timestamped_get, "should not specify a timestamped_get function when get is specified");
2127
+ assert(!this._timestamped_get_func);
2128
+ assert(!this._get_func);
2139
2129
 
2140
- assert(!this._timestamped_set_func);
2141
- assert(!this._set_func);
2130
+ this._get_func = options.get;
2142
2131
 
2143
- this._set_func = turn_sync_to_async(options.set, 1);
2144
- assert(this._set_func.length === 2, " set function must have 2 arguments ( variant, callback)");
2132
+ const timestamped_get_func_from__Variable_bind_with_simple_get = () => {
2133
+ const value: Variant | StatusCode = this._get_func();
2145
2134
 
2146
- this._timestamped_set_func = (
2147
- timestamped_value: DataValue,
2148
- callback: (err: Error | null, statusCode: StatusCode, dataValue: DataValue) => void
2149
- ) => {
2150
- assert(timestamped_value instanceof DataValue);
2151
- this._set_func(timestamped_value.value, (err: Error | null, statusCode: StatusCode) => {
2152
- // istanbul ignore next
2153
- if (!err && !statusCode) {
2135
+ /* istanbul ignore next */
2136
+ if (!is_Variant_or_StatusCode(value)) {
2154
2137
  errorLog(
2155
- chalk.red("UAVariable Binding Error _set_func must return a StatusCode, check the bindVariable parameters")
2138
+ chalk.red(" Bind variable error: "),
2139
+ " : the getter must return a Variant or a StatusCode" + "\nvalue_check.constructor.name ",
2140
+ value ? value.constructor.name : "null"
2141
+ );
2142
+ throw new Error(
2143
+ " bindVariable : the value getter function returns a invalid result ( expecting a Variant or a StatusCode !!!"
2156
2144
  );
2157
- errorLog(chalk.yellow("StatusCode.Good is assumed"));
2158
- return callback(err, StatusCodes.Good, timestamped_value);
2159
2145
  }
2160
- callback(err, statusCode, timestamped_value);
2161
- });
2162
- };
2163
- }
2164
-
2165
- function _Variable_bind_with_timestamped_set(
2166
- this: UAVariableImpl,
2167
- options: {
2168
- timestamped_set: TimestampSetFunc;
2169
- set: undefined;
2170
- }
2171
- ) {
2172
- assert(
2173
- options.timestamped_set.length === 2 || options.timestamped_set.length === 1,
2174
- "timestamped_set must have 2 parameters timestamped_set: function(dataValue,callback){} or one paramater timestamped_set: function(dataValue): Promise<StatusCode>{}"
2175
- );
2176
- assert(!options.set, "should not specify set when timestamped_set_func exists ");
2177
- this._timestamped_set_func = convertToCallbackFunction1<StatusCode, DataValue, UAVariable>(options.timestamped_set);
2178
- }
2179
-
2180
- interface SetterOptions {
2181
- set?: SetFunc;
2182
- timestamped_set?: TimestampSetFunc;
2183
- timestamped_get?: TimestampGetFunc;
2184
- }
2185
- function bind_setter(this: UAVariableImpl, options: SetterOptions) {
2186
- if (typeof options.set === "function") {
2187
- // variation 1
2188
- _Variable_bind_with_simple_set.call(this, options);
2189
- } else if (typeof options.timestamped_set === "function") {
2190
- // variation 2
2191
- assert(typeof options.timestamped_get === "function", "timestamped_set must be used with timestamped_get ");
2192
- _Variable_bind_with_timestamped_set.call(this, {
2193
- set: undefined,
2194
- timestamped_set: options.timestamped_set
2195
- });
2196
- } else if (typeof options.timestamped_get === "function") {
2197
- // timestamped_get is specified but timestamped_set is not
2198
- // => Value is read-only
2199
- _Variable_bind_with_timestamped_set.call(this, {
2200
- set: undefined,
2201
- timestamped_set: _not_writable_timestamped_set_func
2202
- });
2203
- } else {
2204
- _Variable_bind_with_timestamped_set.call(this, {
2205
- set: undefined,
2206
- timestamped_set: _default_writable_timestamped_set_func
2207
- });
2208
- }
2209
- }
2210
-
2211
- interface GetterOptions {
2212
- get?: GetFunc;
2213
- timestamped_get?: TimestampGetFunc;
2214
- refreshFunc?: (callback: CallbackT<DataValue>) => void;
2215
- dataType?: DataType | string;
2216
- value?: any;
2217
- }
2218
- function bind_getter(this: UAVariableImpl, options: GetterOptions) {
2219
- if (typeof options.get === "function") {
2220
- // variation 1
2221
- _Variable_bind_with_simple_get.call(this, options);
2222
- } else if (typeof options.timestamped_get === "function") {
2223
- // variation 2
2146
+ if (is_StatusCode(value)) {
2147
+ return new DataValue({ statusCode: value as StatusCode });
2148
+ } else {
2149
+ if (!this.$dataValue || !this.$dataValue.statusCode.isGoodish() || !sameVariant(this.$dataValue.value, value as Variant)) {
2150
+ this._inner_replace_dataValue(new DataValue({ value }));
2151
+ }
2152
+ return this.$dataValue;
2153
+ }
2154
+ };
2155
+
2224
2156
  _Variable_bind_with_timestamped_get.call(this, {
2225
2157
  get: undefined,
2226
- timestamped_get: options.timestamped_get
2158
+ timestamped_get: timestamped_get_func_from__Variable_bind_with_simple_get
2227
2159
  });
2228
- } else if (typeof options.refreshFunc === "function") {
2229
- // variation 3
2230
- _Variable_bind_with_async_refresh.call(this, options);
2231
- } else {
2160
+ }
2161
+
2162
+ function _Variable_bind_with_simple_set(this: UAVariableImpl, options: any) {
2163
+ assert(this instanceof UAVariableImpl);
2164
+ assert(typeof options.set === "function", "should specify set function");
2165
+ assert(!options.timestamped_set, "should not specify a timestamped_set function");
2166
+
2167
+ assert(!this._timestamped_set_func);
2168
+ assert(!this._set_func);
2169
+
2170
+ this._set_func = turn_sync_to_async(options.set, 1);
2171
+ assert(this._set_func.length === 2, " set function must have 2 arguments ( variant, callback)");
2172
+
2173
+ this._timestamped_set_func = (
2174
+ timestamped_value: DataValue,
2175
+ callback: (err: Error | null, statusCode: StatusCode, dataValue: DataValue) => void
2176
+ ) => {
2177
+ assert(timestamped_value instanceof DataValue);
2178
+ this._set_func(timestamped_value.value, (err: Error | null, statusCode: StatusCode) => {
2179
+ // istanbul ignore next
2180
+ if (!err && !statusCode) {
2181
+ errorLog(
2182
+ chalk.red("UAVariable Binding Error _set_func must return a StatusCode, check the bindVariable parameters")
2183
+ );
2184
+ errorLog(chalk.yellow("StatusCode.Good is assumed"));
2185
+ return callback(err, StatusCodes.Good, timestamped_value);
2186
+ }
2187
+ callback(err, statusCode, timestamped_value);
2188
+ });
2189
+ };
2190
+ }
2191
+
2192
+ function _Variable_bind_with_timestamped_set(
2193
+ this: UAVariableImpl,
2194
+ options: {
2195
+ timestamped_set: TimestampSetFunc;
2196
+ set: undefined;
2197
+ }
2198
+ ) {
2232
2199
  assert(
2233
- !Object.prototype.hasOwnProperty.call(options, "set"),
2234
- "getter is missing : a getter must be provided if a setter is provided"
2200
+ options.timestamped_set.length === 2 || options.timestamped_set.length === 1,
2201
+ "timestamped_set must have 2 parameters timestamped_set: function(dataValue,callback){} or one paramater timestamped_set: function(dataValue): Promise<StatusCode>{}"
2235
2202
  );
2236
- // xx bind_variant.call(this,options);
2237
- if (options.dataType !== undefined) {
2238
- // if (options.dataType !== DataType.ExtensionObject) {
2239
- this.setValueFromSource(options as VariantLike);
2240
- // }
2203
+ assert(!options.set, "should not specify set when timestamped_set_func exists ");
2204
+ this._timestamped_set_func = convertToCallbackFunction1<StatusCode, DataValue, UAVariable>(options.timestamped_set);
2205
+ }
2206
+
2207
+ interface SetterOptions {
2208
+ set?: SetFunc;
2209
+ timestamped_set?: TimestampSetFunc;
2210
+ timestamped_get?: TimestampGetFunc;
2211
+ }
2212
+ function bind_setter(this: UAVariableImpl, options: SetterOptions) {
2213
+ if (typeof options.set === "function") {
2214
+ // variation 1
2215
+ _Variable_bind_with_simple_set.call(this, options);
2216
+ } else if (typeof options.timestamped_set === "function") {
2217
+ // variation 2
2218
+ assert(typeof options.timestamped_get === "function", "timestamped_set must be used with timestamped_get ");
2219
+ _Variable_bind_with_timestamped_set.call(this, {
2220
+ set: undefined,
2221
+ timestamped_set: options.timestamped_set
2222
+ });
2223
+ } else if (typeof options.timestamped_get === "function") {
2224
+ // timestamped_get is specified but timestamped_set is not
2225
+ // => Value is read-only
2226
+ _Variable_bind_with_timestamped_set.call(this, {
2227
+ set: undefined,
2228
+ timestamped_set: _not_writable_timestamped_set_func
2229
+ });
2230
+ } else {
2231
+ _Variable_bind_with_timestamped_set.call(this, {
2232
+ set: undefined,
2233
+ timestamped_set: _default_writable_timestamped_set_func
2234
+ });
2241
2235
  }
2242
2236
  }
2243
- }
2244
-
2245
- export interface UAVariableImplT<T, DT extends DataType> extends UAVariableImpl, UAVariableT<T, DT> {
2246
- on(): any;
2247
- once(): any;
2248
- readValueAsync(context: ISessionContext | null): Promise<DataValueT<T, DT>>;
2249
- readValueAsync(context: ISessionContext | null, callback: CallbackT<DataValueT<T, DT>>): void;
2250
-
2251
- readValue(
2252
- context?: ISessionContext | null,
2253
- indexRange?: NumericRange,
2254
- dataEncoding?: QualifiedNameLike | null
2255
- ): DataValueT<T, DT>;
2256
-
2257
- readValue(
2258
- context?: ISessionContext | null,
2259
- indexRange?: NumericRange,
2260
- dataEncoding?: QualifiedNameLike | null
2261
- ): DataValueT<T, DT>;
2262
-
2263
- writeValue(
2264
- context: ISessionContext,
2265
- dataValue: DataValueT<T, DT>,
2266
- indexRange: NumericRange | null,
2267
- callback: StatusCodeCallback
2268
- ): void;
2269
- writeValue(context: ISessionContext, dataValue: DataValueT<T, DT>, callback: StatusCodeCallback): void;
2270
- writeValue(context: ISessionContext, dataValue: DataValueT<T, DT>, indexRange?: NumericRange | null): Promise<StatusCode>;
2271
- }
2272
- export class UAVariableImplT<T, DT extends DataType> extends UAVariableImpl { }
2237
+
2238
+ interface GetterOptions {
2239
+ get?: GetFunc;
2240
+ timestamped_get?: TimestampGetFunc;
2241
+ refreshFunc?: (callback: CallbackT<DataValue>) => void;
2242
+ dataType?: DataType | string;
2243
+ value?: any;
2244
+ }
2245
+ function bind_getter(this: UAVariableImpl, options: GetterOptions) {
2246
+ if (typeof options.get === "function") {
2247
+ // variation 1
2248
+ _Variable_bind_with_simple_get.call(this, options);
2249
+ } else if (typeof options.timestamped_get === "function") {
2250
+ // variation 2
2251
+ _Variable_bind_with_timestamped_get.call(this, {
2252
+ get: undefined,
2253
+ timestamped_get: options.timestamped_get
2254
+ });
2255
+ } else if (typeof options.refreshFunc === "function") {
2256
+ // variation 3
2257
+ _Variable_bind_with_async_refresh.call(this, options);
2258
+ } else {
2259
+ assert(
2260
+ !Object.prototype.hasOwnProperty.call(options, "set"),
2261
+ "getter is missing : a getter must be provided if a setter is provided"
2262
+ );
2263
+ // xx bind_variant.call(this,options);
2264
+ if (options.dataType !== undefined) {
2265
+ // if (options.dataType !== DataType.ExtensionObject) {
2266
+ this.setValueFromSource(options as VariantLike);
2267
+ // }
2268
+ }
2269
+ }
2270
+ }
2271
+
2272
+ export interface UAVariableImplT<T, DT extends DataType> extends UAVariableImpl, UAVariableT<T, DT> {
2273
+ on(): any;
2274
+ once(): any;
2275
+ readValueAsync(context: ISessionContext | null): Promise<DataValueT<T, DT>>;
2276
+ readValueAsync(context: ISessionContext | null, callback: CallbackT<DataValueT<T, DT>>): void;
2277
+
2278
+ readValue(
2279
+ context?: ISessionContext | null,
2280
+ indexRange?: NumericRange,
2281
+ dataEncoding?: QualifiedNameLike | null
2282
+ ): DataValueT<T, DT>;
2283
+
2284
+ readValue(
2285
+ context?: ISessionContext | null,
2286
+ indexRange?: NumericRange,
2287
+ dataEncoding?: QualifiedNameLike | null
2288
+ ): DataValueT<T, DT>;
2289
+
2290
+ writeValue(
2291
+ context: ISessionContext,
2292
+ dataValue: DataValueT<T, DT>,
2293
+ indexRange: NumericRange | null,
2294
+ callback: StatusCodeCallback
2295
+ ): void;
2296
+ writeValue(context: ISessionContext, dataValue: DataValueT<T, DT>, callback: StatusCodeCallback): void;
2297
+ writeValue(context: ISessionContext, dataValue: DataValueT<T, DT>, indexRange?: NumericRange | null): Promise<StatusCode>;
2298
+ }
2299
+ export class UAVariableImplT<T, DT extends DataType> extends UAVariableImpl { }