node-opcua-address-space 2.90.1 → 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 (91) hide show
  1. package/dist/source/address_space_ts.d.ts +3 -3
  2. package/dist/source/address_space_ts.js +3 -3
  3. package/dist/source/address_space_ts.js.map +1 -1
  4. package/dist/source/helpers/argument_list.js +3 -3
  5. package/dist/source/helpers/argument_list.js.map +1 -1
  6. package/dist/source/helpers/call_helpers.d.ts +1 -1
  7. package/dist/source/helpers/multiform_func.d.ts +8 -8
  8. package/dist/source/interfaces/alarms_and_conditions/ua_condition_ex.d.ts +1 -1
  9. package/dist/source/interfaces/extension_object_constructor.d.ts +1 -1
  10. package/dist/source/interfaces/state_machine/ua_state_machine_type.d.ts +15 -3
  11. package/dist/source/loader/generateAddressSpaceRaw.d.ts +2 -2
  12. package/dist/source/loader/make_xml_extension_object_parser.d.ts +1 -1
  13. package/dist/source/loader/namespace_post_step.d.ts +1 -1
  14. package/dist/source/session_context.d.ts +1 -1
  15. package/dist/src/address_space.js +19 -19
  16. package/dist/src/address_space.js.map +1 -1
  17. package/dist/src/alarms_and_conditions/ua_alarm_condition_impl.js +1 -1
  18. package/dist/src/alarms_and_conditions/ua_alarm_condition_impl.js.map +1 -1
  19. package/dist/src/alarms_and_conditions/ua_condition_impl.js +2 -2
  20. package/dist/src/alarms_and_conditions/ua_condition_impl.js.map +1 -1
  21. package/dist/src/alarms_and_conditions/ua_discrete_alarm_impl.js +1 -1
  22. package/dist/src/alarms_and_conditions/ua_discrete_alarm_impl.js.map +1 -1
  23. package/dist/src/apply_condition_refresh.d.ts +1 -1
  24. package/dist/src/base_node_impl.js +47 -47
  25. package/dist/src/base_node_impl.js.map +1 -1
  26. package/dist/src/base_node_private.js +4 -4
  27. package/dist/src/base_node_private.js.map +1 -1
  28. package/dist/src/event_data.d.ts +2 -2
  29. package/dist/src/extension_object_array_node.js +3 -3
  30. package/dist/src/extension_object_array_node.js.map +1 -1
  31. package/dist/src/index_current.d.ts +1 -1
  32. package/dist/src/index_current.js +2 -1
  33. package/dist/src/index_current.js.map +1 -1
  34. package/dist/src/nodeid_manager.d.ts +3 -3
  35. package/dist/src/nodeset_tools/nodeset_to_xml.d.ts +1 -1
  36. package/dist/src/nodeset_tools/nodeset_to_xml.js +9 -9
  37. package/dist/src/nodeset_tools/nodeset_to_xml.js.map +1 -1
  38. package/dist/src/reference_impl.js +6 -6
  39. package/dist/src/reference_impl.js.map +1 -1
  40. package/dist/src/state_machine/finite_state_machine.d.ts +16 -2
  41. package/dist/src/state_machine/finite_state_machine.js +83 -44
  42. package/dist/src/state_machine/finite_state_machine.js.map +1 -1
  43. package/dist/src/tool_isSubtypeOf.d.ts +18 -0
  44. package/dist/src/tool_isSubtypeOf.js +125 -0
  45. package/dist/src/tool_isSubtypeOf.js.map +1 -0
  46. package/dist/src/ua_data_type_impl.d.ts +6 -4
  47. package/dist/src/ua_data_type_impl.js +24 -22
  48. package/dist/src/ua_data_type_impl.js.map +1 -1
  49. package/dist/src/ua_method_impl.js +6 -6
  50. package/dist/src/ua_method_impl.js.map +1 -1
  51. package/dist/src/ua_object_impl.js +7 -7
  52. package/dist/src/ua_object_impl.js.map +1 -1
  53. package/dist/src/ua_object_type_impl.d.ts +4 -2
  54. package/dist/src/ua_object_type_impl.js +12 -10
  55. package/dist/src/ua_object_type_impl.js.map +1 -1
  56. package/dist/src/ua_reference_type_impl.d.ts +5 -3
  57. package/dist/src/ua_reference_type_impl.js +12 -10
  58. package/dist/src/ua_reference_type_impl.js.map +1 -1
  59. package/dist/src/ua_variable_impl.d.ts +7 -7
  60. package/dist/src/ua_variable_impl.js +30 -30
  61. package/dist/src/ua_variable_impl.js.map +1 -1
  62. package/dist/src/ua_variable_impl_ext_obj.js +3 -3
  63. package/dist/src/ua_variable_impl_ext_obj.js.map +1 -1
  64. package/dist/src/ua_variable_type_impl.d.ts +4 -2
  65. package/dist/src/ua_variable_type_impl.js +15 -10
  66. package/dist/src/ua_variable_type_impl.js.map +1 -1
  67. package/dist/src/ua_view_impl.js +3 -3
  68. package/dist/src/ua_view_impl.js.map +1 -1
  69. package/distHelpers/boiler_system.js +5 -5
  70. package/distHelpers/boiler_system.js.map +1 -1
  71. package/package.json +5 -5
  72. package/source/helpers/argument_list.ts +3 -3
  73. package/source/interfaces/state_machine/ua_state_machine_type.ts +19 -1
  74. package/src/address_space.ts +8 -8
  75. package/src/alarms_and_conditions/ua_alarm_condition_impl.ts +1 -1
  76. package/src/alarms_and_conditions/ua_condition_impl.ts +2 -2
  77. package/src/alarms_and_conditions/ua_discrete_alarm_impl.ts +1 -1
  78. package/src/base_node_impl.ts +3 -3
  79. package/src/base_node_private.ts +3 -3
  80. package/src/extension_object_array_node.ts +3 -3
  81. package/src/index_current.ts +1 -1
  82. package/src/nodeset_tools/nodeset_to_xml.ts +9 -9
  83. package/src/state_machine/finite_state_machine.ts +105 -60
  84. package/src/{tool_isSupertypeOf.ts → tool_isSubtypeOf.ts} +9 -9
  85. package/src/ua_data_type_impl.ts +21 -19
  86. package/src/ua_object_type_impl.ts +9 -3
  87. package/src/ua_reference_type_impl.ts +7 -4
  88. package/src/ua_variable_impl.ts +2033 -2033
  89. package/src/ua_variable_impl_ext_obj.ts +3 -3
  90. package/src/ua_variable_type_impl.ts +11 -4
  91. package/test_helpers/boiler_system.ts +5 -5
@@ -1,2299 +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());
238
- }
239
239
 
240
- return dest_isSuperTypeOf_variant;
241
- }
240
+ return dest_isSubTypeOf_variant;
241
+ }
242
242
 
243
- function default_func(this: UAVariable, dataValue1: DataValue, callback1: CallbackT<StatusCode>) {
244
- return _default_writable_timestamped_set_func.call(this, dataValue1, callback1);
245
- }
243
+ function default_func(this: UAVariable, dataValue1: DataValue, callback1: CallbackT<StatusCode>) {
244
+ return _default_writable_timestamped_set_func.call(this, dataValue1, callback1);
245
+ }
246
246
 
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
- }
268
-
269
- /**
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
- *
294
- */
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;
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
+ }
306
268
 
307
269
  /**
308
- * @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
+ *
309
294
  */
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
- );
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;
333
335
  }
334
- return super.typeDefinitionObj as UAVariableType;
335
- }
336
- get typeDefinition(): NodeId {
337
- return super.typeDefinition;
338
- }
339
- constructor(options: UAVariableOptions) {
340
- super(options);
336
+ get typeDefinition(): NodeId {
337
+ return super.typeDefinition;
338
+ }
339
+ constructor(options: UAVariableOptions) {
340
+ super(options);
341
341
 
342
- verifyRankAndDimensions(options);
343
- this.valueRank = options.valueRank!;
344
- this.arrayDimensions = options.arrayDimensions!;
342
+ verifyRankAndDimensions(options);
343
+ this.valueRank = options.valueRank!;
344
+ this.arrayDimensions = options.arrayDimensions!;
345
345
 
346
- this.dataType = this.resolveNodeId(options.dataType); // DataType (NodeId)
346
+ this.dataType = this.resolveNodeId(options.dataType); // DataType (NodeId)
347
347
 
348
- this.accessLevel = adjust_accessLevel(options.accessLevel);
348
+ this.accessLevel = adjust_accessLevel(options.accessLevel);
349
349
 
350
- this.userAccessLevel = adjust_userAccessLevel(options.userAccessLevel, this.accessLevel);
350
+ this.userAccessLevel = adjust_userAccessLevel(options.userAccessLevel, this.accessLevel);
351
351
 
352
- this.minimumSamplingInterval = adjust_samplingInterval(options.minimumSamplingInterval || 0);
352
+ this.minimumSamplingInterval = adjust_samplingInterval(options.minimumSamplingInterval || 0);
353
353
 
354
- this.historizing = !!options.historizing; // coerced to boolean"
354
+ this.historizing = !!options.historizing; // coerced to boolean"
355
355
 
356
- this.$dataValue = new DataValue({ statusCode: StatusCodes.UncertainInitialValue, value: { dataType: DataType.Null } });
356
+ this.$dataValue = new DataValue({ statusCode: StatusCodes.UncertainInitialValue, value: { dataType: DataType.Null } });
357
357
 
358
- if (options.value) {
359
- this.bindVariable(options.value);
360
- }
358
+ if (options.value) {
359
+ this.bindVariable(options.value);
360
+ }
361
361
 
362
- this.setMaxListeners(5000);
362
+ this.setMaxListeners(5000);
363
363
 
364
- this.semantic_version = 0;
365
- }
366
- private checkAccessLevelPrivate(
367
- _context: ISessionContext,
368
- accessLevel: AccessLevelFlag): boolean {
369
- if (this.userAccessLevel === undefined) {
370
- return true;
364
+ this.semantic_version = 0;
371
365
  }
372
- return (this.userAccessLevel & accessLevel) === accessLevel;
373
- }
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;
366
+ private checkAccessLevelPrivate(
367
+ _context: ISessionContext,
368
+ accessLevel: AccessLevelFlag): boolean {
369
+ if (this.userAccessLevel === undefined) {
370
+ return true;
387
371
  }
372
+ return (this.userAccessLevel & accessLevel) === accessLevel;
388
373
  }
389
- return true;
390
- }
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);
399
- }
400
-
401
- public isReadable(context: ISessionContext): boolean {
402
- return (this.accessLevel & AccessLevelFlag.CurrentRead) === AccessLevelFlag.CurrentRead;
403
- }
404
-
405
- public isUserReadable(context: ISessionContext): boolean {
406
- if (!this.isReadable(context)) {
407
- 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;
408
390
  }
409
- if (!this.checkPermissionPrivate(context, PermissionType.Read)) {
410
- 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);
411
399
  }
412
- return this.checkAccessLevelPrivate(context, AccessLevelFlag.CurrentRead);
413
- }
414
400
 
415
- public isWritable(context: ISessionContext): boolean {
416
- return (this.accessLevel & AccessLevelFlag.CurrentWrite) === AccessLevelFlag.CurrentWrite;
417
- }
418
-
419
- public isUserWritable(context: ISessionContext): boolean {
420
- if (!this.isWritable(context)) {
421
- return false;
401
+ public isReadable(context: ISessionContext): boolean {
402
+ return (this.accessLevel & AccessLevelFlag.CurrentRead) === AccessLevelFlag.CurrentRead;
422
403
  }
423
- return this.checkPermissionAndAccessLevelPrivate(context, PermissionType.Write, AccessLevelFlag.CurrentWrite);
424
- }
425
404
 
426
- public canUserReadHistory(context: ISessionContext): boolean {
427
- return this.checkPermissionAndAccessLevelPrivate(context, PermissionType.ReadHistory, AccessLevelFlag.HistoryRead);
428
- }
429
- public canUserWriteHistorizingAttribute(context: ISessionContext): boolean {
430
- if (context && !context.checkPermission(this, PermissionType.WriteHistorizing)) {
431
- 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);
432
413
  }
433
- return true;
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
414
 
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;
415
+ public isWritable(context: ISessionContext): boolean {
416
+ return (this.accessLevel & AccessLevelFlag.CurrentWrite) === AccessLevelFlag.CurrentWrite;
479
417
  }
480
418
 
481
- if (context.isAccessRestricted(this)) {
482
- 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);
483
424
  }
484
425
 
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 });
426
+ public canUserReadHistory(context: ISessionContext): boolean {
427
+ return this.checkPermissionAndAccessLevelPrivate(context, PermissionType.ReadHistory, AccessLevelFlag.HistoryRead);
490
428
  }
491
- if (!this.isUserReadable(context)) {
492
- return new DataValue({ statusCode: StatusCodes.BadNotReadable });
493
- }
494
- if (!isValidDataEncoding(dataEncoding)) {
495
- 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;
496
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
+ }
497
497
 
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);
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 !");
506
509
  }
507
- } else {
508
- errorLog("Unsupported: _timestamped_get_func returns a Promise !");
509
510
  }
510
511
  }
511
- }
512
512
 
513
- let dataValue = this.$dataValue;
513
+ let dataValue = this.$dataValue;
514
514
 
515
- if (dataValue.statusCode.isGoodish()) {
516
- // note : extractRange will clone the dataValue
517
- dataValue = extractRange(dataValue, indexRange);
518
- }
515
+ if (dataValue.statusCode.isGoodish()) {
516
+ // note : extractRange will clone the dataValue
517
+ dataValue = extractRange(dataValue, indexRange);
518
+ }
519
519
 
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
- );
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;
532
534
  }
533
- return dataValue;
534
- }
535
-
536
- public isEnumeration(): boolean {
537
- return this.addressSpacePrivate.isEnumeration(this.dataType);
538
- }
539
535
 
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");
536
+ public isEnumeration(): boolean {
537
+ return this.addressSpacePrivate.isEnumeration(this.dataType);
546
538
  }
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.isSupertypeOf(structureNode);
552
- }
553
539
 
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();
559
- }
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
+ }
560
553
 
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);
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();
566
559
  }
567
560
 
568
- const oldestDate = args[0] as Date;
569
- assert(oldestDate instanceof Date);
570
- 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
+ }
571
567
 
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
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;
581
589
  return callback(null, dataValue);
582
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
+ }
583
615
  }
584
616
 
585
- if (this.$dataValue.serverTimestamp && oldestDate.getTime() <= this.$dataValue.serverTimestamp!.getTime()) {
586
- const dataValue = this.readValue();
587
- dataValue.serverTimestamp = oldestDate;
588
- dataValue.serverPicoseconds = 0;
589
- return callback(null, 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 : "?????" };
590
622
  }
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
- }
615
- }
616
623
 
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 : "?????" };
622
- }
623
-
624
- public writeEnumValue(value: string | number): void {
625
- const enumInfo = this._getEnumerationInfo();
624
+ public writeEnumValue(value: string | number): void {
625
+ const enumInfo = this._getEnumerationInfo();
626
626
 
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 + "]");
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;
631
634
  }
632
- const valueIndex = enumInfo.nameIndex[value].value;
633
- value = valueIndex;
634
- }
635
- if (isFinite(value)) {
636
- const possibleValues = Object.keys(enumInfo.nameIndex).join(",");
635
+ if (isFinite(value)) {
636
+ const possibleValues = Object.keys(enumInfo.nameIndex).join(",");
637
637
 
638
- if (!enumInfo.valueIndex[value]) {
639
- 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");
640
647
  }
641
- this.setValueFromSource({
642
- dataType: DataType.Int32,
643
- value
644
- });
645
- } else {
646
- throw new Error("UAVariable#writeEnumValue: value type mismatch");
647
648
  }
648
- }
649
649
 
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);
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);
658
658
 
659
- const options: DataValueLike = {};
659
+ const options: DataValueLike = {};
660
660
 
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);
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
+ }
669
670
  }
670
- }
671
671
 
672
- switch (attributeId) {
673
- case AttributeIds.Value:
674
- return this.readValue(context, indexRange, dataEncoding);
672
+ switch (attributeId) {
673
+ case AttributeIds.Value:
674
+ return this.readValue(context, indexRange, dataEncoding);
675
675
 
676
- case AttributeIds.DataType:
677
- return this._readDataType();
676
+ case AttributeIds.DataType:
677
+ return this._readDataType();
678
678
 
679
- case AttributeIds.ValueRank:
680
- return this._readValueRank();
679
+ case AttributeIds.ValueRank:
680
+ return this._readValueRank();
681
681
 
682
- case AttributeIds.ArrayDimensions:
683
- return this._readArrayDimensions();
682
+ case AttributeIds.ArrayDimensions:
683
+ return this._readArrayDimensions();
684
684
 
685
- case AttributeIds.AccessLevel:
686
- return this._readAccessLevel(context);
685
+ case AttributeIds.AccessLevel:
686
+ return this._readAccessLevel(context);
687
687
 
688
- case AttributeIds.UserAccessLevel:
689
- return this._readUserAccessLevel(context);
688
+ case AttributeIds.UserAccessLevel:
689
+ return this._readUserAccessLevel(context);
690
690
 
691
- case AttributeIds.MinimumSamplingInterval:
692
- return this._readMinimumSamplingInterval();
691
+ case AttributeIds.MinimumSamplingInterval:
692
+ return this._readMinimumSamplingInterval();
693
693
 
694
- case AttributeIds.Historizing:
695
- return this._readHistorizing();
694
+ case AttributeIds.Historizing:
695
+ return this._readHistorizing();
696
696
 
697
- case AttributeIds.AccessLevelEx:
698
- return this._readAccessLevelEx(context);
699
- default:
700
- 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
+ }
701
702
  }
702
- }
703
703
 
704
- public getBasicDataType(): DataType {
705
- if (this._basicDataType) {
706
- 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;
707
722
  }
708
- if (this.dataType.namespace === 0 && this.dataType.value === 0) {
709
- return DataType.Null;
723
+ public adjustVariant(variant: Variant): Variant {
724
+ return adjustVariant(variant, this.valueRank, this.getBasicDataType());
710
725
  }
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;
722
- }
723
- public adjustVariant(variant: Variant): Variant {
724
- return adjustVariant(variant, this.valueRank, this.getBasicDataType());
725
- }
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
- );
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
+ }
737
762
  }
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
- );
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
748
770
  }
771
+
749
772
  if (
750
- this.dataType.namespace === 0 &&
751
- this.dataType.value === DataType.LocalizedText &&
752
- variant.dataType !== DataType.LocalizedText &&
753
- variant.dataType !== DataType.Null
773
+ basicType !== DataType.Null &&
774
+ basicType !== DataType.Variant &&
775
+ variant.dataType !== DataType.Null &&
776
+ variant.dataType !== basicType
754
777
  ) {
755
- throw new Error(
756
- "Variant must provide a valid LocalizedText : variant = " +
757
- variant.toString() +
758
- " this.dataType= " +
759
- this.dataType.toString()
760
- );
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);
761
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;
762
802
  }
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
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;
770
856
  }
771
-
772
- if (
773
- basicType !== DataType.Null &&
774
- basicType !== DataType.Variant &&
775
- variant.dataType !== DataType.Null &&
776
- variant.dataType !== basicType
777
- ) {
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);
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;
802
857
  }
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
858
 
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;
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;
845
881
  } else {
846
- this.$dataValue = dataValue;
882
+ const { timestamp, picoseconds } = getCurrentClock();
883
+ dataValue.sourceTimestamp = timestamp;
884
+ dataValue.sourcePicoseconds = picoseconds;
847
885
  }
848
- } else {
849
- this._internal_set_dataValue(dataValue);
850
886
  }
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;
856
- }
857
- }
858
887
 
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);
888
+ if (context.currentTime && !dataValue.serverTimestamp) {
889
+ dataValue.serverTimestamp = context.currentTime.timestamp;
890
+ dataValue.serverPicoseconds = context.currentTime.picoseconds;
891
+ }
874
892
 
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;
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];
881
902
  } else {
882
- const { timestamp, picoseconds } = getCurrentClock();
883
- dataValue.sourceTimestamp = timestamp;
884
- dataValue.sourcePicoseconds = picoseconds;
903
+ throw new Error("Invalid Number of args");
885
904
  }
886
- }
887
905
 
888
- if (context.currentTime && !dataValue.serverTimestamp) {
889
- dataValue.serverTimestamp = context.currentTime.timestamp;
890
- dataValue.serverPicoseconds = context.currentTime.picoseconds;
891
- }
892
-
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];
902
- } else {
903
- throw new Error("Invalid Number of args");
904
- }
906
+ assert(typeof callback === "function");
907
+ assert(dataValue instanceof DataValue);
908
+ // index range could be string
909
+ indexRange = NumericRange.coerce(indexRange);
905
910
 
906
- assert(typeof callback === "function");
907
- assert(dataValue instanceof DataValue);
908
- // index range could be string
909
- indexRange = NumericRange.coerce(indexRange);
910
-
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
- }
918
-
919
-
920
- if (!this.isUserWritable(context)) {
921
- return callback!(null, StatusCodes.BadWriteNotSupported);
922
- }
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
+ }
923
918
 
924
- // adjust special case
925
- const variant = adjustVariant2.call(this, dataValue.value);
926
919
 
927
- const statusCode = this.checkVariantCompatibility(variant);
928
- if (statusCode.isNot(StatusCodes.Good)) {
929
- return callback!(null, statusCode);
930
- }
920
+ if (!this.isUserWritable(context)) {
921
+ return callback!(null, StatusCodes.BadWriteNotSupported);
922
+ }
931
923
 
932
- const write_func = this._timestamped_set_func || default_func;
924
+ // adjust special case
925
+ const variant = adjustVariant2.call(this, dataValue.value);
933
926
 
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);
927
+ const statusCode = this.checkVariantCompatibility(variant);
928
+ if (statusCode.isNot(StatusCodes.Good)) {
929
+ return callback!(null, statusCode);
930
+ }
940
931
 
941
- write_func.call(this, dataValue, (err?: Error | null, statusCode1?: StatusCode) => {
942
- if (!err) {
943
- dataValue && this.verifyVariantCompatibility(dataValue.value);
932
+ const write_func = this._timestamped_set_func || default_func;
944
933
 
945
- if (indexRange && !indexRange.isEmpty()) {
946
- if (!indexRange.isValid()) {
947
- return callback!(null, StatusCodes.BadIndexRangeInvalid);
948
- }
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);
949
940
 
950
- 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);
951
944
 
952
- if (dataValue.value.arrayType === VariantArrayType.Array) {
953
- if (this.$dataValue.value.arrayType !== VariantArrayType.Array) {
954
- return callback(null, StatusCodes.BadTypeMismatch);
945
+ if (indexRange && !indexRange.isEmpty()) {
946
+ if (!indexRange.isValid()) {
947
+ return callback!(null, StatusCodes.BadIndexRangeInvalid);
955
948
  }
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
949
 
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 !
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 {
972
991
  return callback!(null, StatusCodes.BadTypeMismatch);
973
992
  }
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 {
991
- return callback!(null, StatusCodes.BadTypeMismatch);
992
993
  }
993
- }
994
- try {
995
- this._internal_set_dataValue(dataValue, indexRange);
996
- } catch (err) {
997
- if (err instanceof Error) {
998
- 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);
999
1001
  }
1000
- return callback!(null, StatusCodes.BadInternalError);
1001
- }
1002
- }
1003
- callback!(err || null, statusCode1);
1004
- });
1005
- }
1006
-
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
- }
1018
-
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
- }
1050
-
1051
- if (!this.canUserWriteHistorizingAttribute(context)) {
1052
- return callback(null, StatusCodes.BadHistoryOperationUnsupported);
1053
- }
1054
-
1055
- // if the variable has no historizing in place reject
1056
- if (!this.getChildByName("HA Configuration")) {
1057
- return callback(null, StatusCodes.BadNotSupported);
1058
1002
  }
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
- }
1069
-
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;
1003
+ callback!(err || null, statusCode1);
1004
+ });
1085
1005
  }
1086
- return StatusCodes.Good;
1087
- }
1088
1006
 
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
- }
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
+ }
1097
1018
 
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
- }
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
+ }
1228
1050
 
1229
- options = options || {};
1051
+ if (!this.canUserWriteHistorizingAttribute(context)) {
1052
+ return callback(null, StatusCodes.BadHistoryOperationUnsupported);
1053
+ }
1230
1054
 
1231
- assert(typeof this._timestamped_set_func !== "function", "UAVariable already bound");
1232
- 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
+ }
1233
1069
 
1234
- bind_getter.call(this, options);
1235
- 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
+ }
1236
1228
 
1237
- const _historyRead = options.historyRead;
1238
- if (_historyRead) {
1239
- assert(typeof this._historyRead !== "function" || this._historyRead === UAVariableImpl.prototype._historyRead);
1240
- assert(typeof _historyRead === "function");
1229
+ options = options || {};
1241
1230
 
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
- }
1231
+ assert(typeof this._timestamped_set_func !== "function", "UAVariable already bound");
1232
+ assert(typeof this._timestamped_get_func !== "function", "UAVariable already bound");
1249
1233
 
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");
1234
+ bind_getter.call(this, options);
1235
+ bind_setter.call(this, options);
1260
1236
 
1261
- this.__waiting_callbacks = this.__waiting_callbacks || [];
1262
- 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");
1263
1241
 
1264
- const _readValueAsync_in_progress = this.__waiting_callbacks.length >= 2;
1265
- if (_readValueAsync_in_progress) {
1266
- return;
1267
- }
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");
1268
1260
 
1269
- const readImmediate = (innerCallback: (err: Error | null, dataValue: DataValue) => void) => {
1270
- assert(this.$dataValue instanceof DataValue);
1271
- const dataValue = this.readValue();
1272
- innerCallback(null, dataValue);
1273
- };
1261
+ this.__waiting_callbacks = this.__waiting_callbacks || [];
1262
+ this.__waiting_callbacks.push(callback);
1274
1263
 
1275
- 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
+ }
1276
1268
 
1277
- if (!this.isReadable(context)) {
1278
- func = (innerCallback: (err: Error | null, dataValue: DataValue) => void) => {
1279
- 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();
1280
1272
  innerCallback(null, dataValue);
1281
1273
  };
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;
1294
- }
1295
-
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
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;
1304
1294
  }
1305
- };
1306
1295
 
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);
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
+ }
1315
1316
  }
1317
+ satisfy_callbacks(err as Error);
1316
1318
  }
1317
- satisfy_callbacks(err as Error);
1318
1319
  }
1319
- }
1320
1320
 
1321
- public getWriteMask(): number {
1322
- return super.getWriteMask();
1323
- }
1321
+ public getWriteMask(): number {
1322
+ return super.getWriteMask();
1323
+ }
1324
1324
 
1325
- public getUserWriteMask(): number {
1326
- return super.getUserWriteMask();
1327
- }
1325
+ public getUserWriteMask(): number {
1326
+ return super.getUserWriteMask();
1327
+ }
1328
1328
 
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,
1334
-
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
- };
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,
1334
+
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
+ };
1343
1343
 
1344
- const newVariable = _clone.call(
1345
- this,
1346
- UAVariableImpl,
1347
- options,
1348
- optionalFilter || defaultCloneFilter,
1349
- extraInfo || defaultCloneExtraInfo
1350
- ) as UAVariableImpl;
1344
+ const newVariable = _clone.call(
1345
+ this,
1346
+ UAVariableImpl,
1347
+ options,
1348
+ optionalFilter || defaultCloneFilter,
1349
+ extraInfo || defaultCloneExtraInfo
1350
+ ) as UAVariableImpl;
1351
1351
 
1352
- newVariable.bindVariable();
1352
+ newVariable.bindVariable();
1353
1353
 
1354
- assert(typeof newVariable._timestamped_set_func === "function");
1354
+ assert(typeof newVariable._timestamped_set_func === "function");
1355
1355
 
1356
- assert(newVariable.dataType === this.dataType);
1357
- newVariable.$dataValue = this.$dataValue.clone();
1356
+ assert(newVariable.dataType === this.dataType);
1357
+ newVariable.$dataValue = this.$dataValue.clone();
1358
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;
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
+ }
1373
1374
  }
1375
+ return newVariable;
1374
1376
  }
1375
- return newVariable;
1376
- }
1377
1377
 
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());
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;
1384
1386
  }
1385
- return dt as UADataType;
1386
- }
1387
1387
 
1388
- public get dataTypeObj(): UADataType {
1389
- return this.getDataTypeNode();
1390
- }
1391
-
1392
- public checkExtensionObjectIsCorrect(extObj: ExtensionObject | ExtensionObject[] | null): boolean {
1393
- if (!extObj) {
1394
- return true;
1388
+ public get dataTypeObj(): UADataType {
1389
+ return this.getDataTypeNode();
1395
1390
  }
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
1391
 
1406
- if (this.valueRank === -1) {
1407
- /** Scalar */
1408
- if (extObj instanceof Array) {
1409
- return false;
1392
+ public checkExtensionObjectIsCorrect(extObj: ExtensionObject | ExtensionObject[] | null): boolean {
1393
+ if (!extObj) {
1394
+ return true;
1410
1395
  }
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 {
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) {
1422
1409
  return false;
1423
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
+ );
1424
1436
  }
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
- );
1436
- }
1437
- function checkExtensionObjectIsCorrectScalar(
1438
- this: UAVariableImpl,
1439
- extObj: ExtensionObject | ExtensionObject[] | null
1440
- ): boolean {
1441
- // istanbul ignore next
1442
- if (!(extObj && extObj.constructor)) {
1443
- errorLog(extObj);
1444
- throw new Error("expecting an valid extension object");
1445
- }
1446
- return extObj.constructor.name === Constructor.name;
1447
- }
1448
-
1449
- function checkExtensionObjectIsCorrectArray(this: UAVariableImpl, extObjArray: ExtensionObject[]): boolean {
1450
- // istanbul ignore next
1451
- for (const extObj of extObjArray) {
1437
+ function checkExtensionObjectIsCorrectScalar(
1438
+ this: UAVariableImpl,
1439
+ extObj: ExtensionObject | ExtensionObject[] | null
1440
+ ): boolean {
1441
+ // istanbul ignore next
1452
1442
  if (!(extObj && extObj.constructor)) {
1453
1443
  errorLog(extObj);
1454
1444
  throw new Error("expecting an valid extension object");
1455
1445
  }
1446
+ return extObj.constructor.name === Constructor.name;
1456
1447
  }
1457
- try {
1458
- for (const e of extObjArray) {
1459
- if (!e) {
1460
- 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");
1461
1455
  }
1462
- if (e.constructor.name !== Constructor.name) {
1463
- debugLog("extObj.constructor.name ", e.constructor.name, "expected", Constructor.name);
1464
- 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
+ }
1465
1466
  }
1467
+ return true;
1468
+ } catch (err) {
1469
+ errorLog(err);
1470
+ return false;
1466
1471
  }
1467
- return true;
1468
- } catch (err) {
1469
- errorLog(err);
1470
- return false;
1471
1472
  }
1472
1473
  }
1473
- }
1474
1474
 
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];
1513
- }
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];
1513
+ }
1514
1514
 
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);
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;
1522
1524
  }
1523
- return _bindExtensionObject(this, optionalExtensionObject, options) as ExtensionObject;
1524
1525
  }
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
- }
1604
-
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
- }
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
+ }
1629
1604
 
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)) {
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
+ }
1651
1656
  const result = new HistoryReadResult({
1652
1657
  statusCode: StatusCodes.BadHistoryOperationUnsupported
1653
1658
  });
1654
1659
  callback(null, result);
1655
1660
  }
1656
- const result = new HistoryReadResult({
1657
- statusCode: StatusCodes.BadHistoryOperationUnsupported
1658
- });
1659
- callback(null, result);
1660
- }
1661
-
1662
- public _historyPush(newDataValue: DataValue): any {
1663
- throw new Error("");
1664
- }
1665
1661
 
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("");
1674
- }
1662
+ public _historyPush(newDataValue: DataValue): any {
1663
+ throw new Error("");
1664
+ }
1675
1665
 
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
- }
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("");
1674
+ }
1686
1675
 
1687
- public _update_startOfOnlineArchive(newDate: Date): void {
1688
- // please install
1689
- throw new Error("");
1690
- }
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
+ }
1691
1686
 
1692
- public _update_startOfArchive(newDate: Date): void {
1693
- throw new Error("");
1694
- }
1687
+ public _update_startOfOnlineArchive(newDate: Date): void {
1688
+ // please install
1689
+ throw new Error("");
1690
+ }
1695
1691
 
1696
- public _validate_DataType(variantDataType: DataType): boolean {
1697
- return validateDataType(this.addressSpace, this.dataType, variantDataType, this.nodeId, /* allow Nulls */ false);
1698
- }
1692
+ public _update_startOfArchive(newDate: Date): void {
1693
+ throw new Error("");
1694
+ }
1699
1695
 
1700
- public _internal_set_value(value: Variant): void {
1701
- if (value.dataType !== DataType.Null) {
1702
- this.verifyVariantCompatibility(value);
1696
+ public _validate_DataType(variantDataType: DataType): boolean {
1697
+ return validateDataType(this.addressSpace, this.dataType, variantDataType, this.nodeId, /* allow Nulls */ false);
1703
1698
  }
1704
- this.$dataValue.value = value;
1705
- }
1706
1699
 
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");
1700
+ public _internal_set_value(value: Variant): void {
1701
+ if (value.dataType !== DataType.Null) {
1702
+ this.verifyVariantCompatibility(value);
1703
+ }
1704
+ this.$dataValue.value = value;
1705
+ }
1711
1706
 
1712
- const addressSpace = this.addressSpace;
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");
1713
1711
 
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
- }
1712
+ const addressSpace = this.addressSpace;
1719
1713
 
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
- );
1714
+ // istanbul ignore next
1715
+ if (!addressSpace) {
1716
+ warningLog("UAVariable#_internal_set_dataValue : no addressSpace ! may be node has already been deleted ?");
1717
+ return;
1731
1718
  }
1732
- }
1733
1719
 
1734
- // istanbul ignore next
1735
- if (dataValue.value.dataType === DataType.ExtensionObject) {
1736
1720
  // 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());
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
+ }
1740
1732
  }
1741
- }
1742
1733
 
1743
- this.verifyVariantCompatibility(dataValue.value);
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
+ }
1741
+ }
1744
1742
 
1745
- this._inner_replace_dataValue(dataValue, indexRange);
1746
- }
1743
+ this.verifyVariantCompatibility(dataValue.value);
1747
1744
 
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;
1774
- const preciseClock = coerceClock(this.$dataValue.sourceTimestamp, this.$dataValue.sourcePicoseconds);
1775
- this.$set_ExtensionObject(dataValue.value.value, preciseClock, new Set())
1776
- } else {
1777
- this.$dataValue = dataValue;
1778
- this.$dataValue.statusCode = this.$dataValue.statusCode || StatusCodes.Good;
1779
- }
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;
1745
+ this._inner_replace_dataValue(dataValue, indexRange);
1789
1746
  }
1790
1747
 
1791
- if (!sameDataValue(old_dataValue, dataValue)) {
1792
- 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;
1793
1774
  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);
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
+ }
1798
1802
  } else {
1799
- this.touchValue(preciseClock);
1800
- propagateTouchValueDownward(this, preciseClock, cache);
1803
+ this.emit("value_changed", this.$dataValue, indexRange);
1801
1804
  }
1802
- } else {
1803
- this.emit("value_changed", this.$dataValue, indexRange);
1804
1805
  }
1805
1806
  }
1806
- }
1807
1807
 
1808
- public _conditionRefresh(_cache?: ConditionRefreshCache): void {
1809
- apply_condition_refresh.call(this, _cache);
1810
- }
1808
+ public _conditionRefresh(_cache?: ConditionRefreshCache): void {
1809
+ apply_condition_refresh.call(this, _cache);
1810
+ }
1811
1811
 
1812
- public handle_semantic_changed(): void {
1813
- this.semantic_version = this.semantic_version + 1;
1814
- this.emit("semantic_changed");
1815
- }
1812
+ public handle_semantic_changed(): void {
1813
+ this.semantic_version = this.semantic_version + 1;
1814
+ this.emit("semantic_changed");
1815
+ }
1816
1816
 
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
- }
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
+ }
1828
1828
 
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
- }
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
+ }
1837
1837
 
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
- }
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
+ }
1847
1847
 
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
- }
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
+ }
1856
1856
 
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
- }
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
+ }
1866
1866
 
1867
- private _readUserAccessLevel(context: ISessionContext): DataValue {
1868
- assert(context instanceof SessionContext);
1867
+ private _readUserAccessLevel(context: ISessionContext): DataValue {
1868
+ assert(context instanceof SessionContext);
1869
1869
 
1870
- const effectiveUserAccessLevel = _calculateEffectiveUserAccessLevelFromPermission(this, context, this.userAccessLevel);
1870
+ const effectiveUserAccessLevel = _calculateEffectiveUserAccessLevelFromPermission(this, context, this.userAccessLevel);
1871
1871
 
1872
- const options = {
1873
- value: {
1874
- dataType: DataType.Byte,
1875
- statusCode: StatusCodes.Good,
1876
- 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;
1877
1890
  }
1878
- };
1879
- return new DataValue(options);
1880
- }
1891
+ return new DataValue(options);
1892
+ }
1881
1893
 
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;
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);
1890
1901
  }
1891
- return new DataValue(options);
1892
1902
  }
1893
1903
 
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);
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;
1901
1918
  }
1902
- }
1903
-
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;
1918
- }
1919
- export interface UAVariableImpl extends UAVariableImplExtArray {
1920
- }
1921
- function check_valid_array(dataType: DataType, array: any): boolean {
1922
- if (Array.isArray(array)) {
1923
- return true;
1919
+ export interface UAVariableImpl extends UAVariableImplExtArray {
1924
1920
  }
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;
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;
1942
1944
  }
1943
- return false;
1944
- }
1945
1945
 
1946
- function _apply_default_timestamps(dataValue: DataValue): void {
1947
- const now = getCurrentClock();
1948
- 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
+ }
1949
1959
 
1950
- if (!dataValue.sourceTimestamp) {
1951
- dataValue.sourceTimestamp = now.timestamp;
1952
- dataValue.sourcePicoseconds = now.picoseconds;
1960
+ function unsetFlag(flags: number, mask: number): number {
1961
+ return flags & ~mask;
1953
1962
  }
1954
- if (!dataValue.serverTimestamp) {
1955
- dataValue.serverTimestamp = now.timestamp;
1956
- dataValue.serverPicoseconds = now.picoseconds;
1963
+ function setFlag(flags: number, mask: number): number {
1964
+ return flags | mask;
1957
1965
  }
1958
- }
1959
-
1960
- function unsetFlag(flags: number, mask: number): number {
1961
- return flags & ~mask;
1962
- }
1963
- function setFlag(flags: number, mask: number): number {
1964
- return flags | mask;
1965
- }
1966
-
1967
- function _calculateEffectiveUserAccessLevelFromPermission(
1968
- node: UAVariable,
1969
- context: ISessionContext,
1970
- userAccessLevel: AccessLevelFlag | undefined
1971
- ): AccessLevelFlag {
1972
- function __adjustFlag(
1973
- permissionType: PermissionType,
1974
- access: AccessLevelFlag,
1975
- userAccessLevel1: AccessLevelFlag
1966
+
1967
+ function _calculateEffectiveUserAccessLevelFromPermission(
1968
+ node: UAVariable,
1969
+ context: ISessionContext,
1970
+ userAccessLevel: AccessLevelFlag | undefined
1976
1971
  ): AccessLevelFlag {
1977
- if ((node.accessLevel & access) === 0 || (userAccessLevel1 & access) === 0) {
1978
- userAccessLevel1 = unsetFlag(userAccessLevel1, access);
1979
- } else {
1980
- 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) {
1981
1978
  userAccessLevel1 = unsetFlag(userAccessLevel1, access);
1979
+ } else {
1980
+ if (!context.checkPermission(node, permissionType)) {
1981
+ userAccessLevel1 = unsetFlag(userAccessLevel1, access);
1982
+ }
1982
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;
1983
1998
  }
1984
- return userAccessLevel1;
1985
1999
  }
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;
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;
1998
2007
  }
1999
- }
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;
2007
- }
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);
2015
- }
2016
-
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
- }
2024
-
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;
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);
2036
2015
  }
2037
- }
2038
2016
 
2039
- 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
+ }
2040
2024
 
2041
- function coerceDataValue(dataValue: DataValue | DataValueLike): DataValue {
2042
- if (dataValue instanceof DataValue) {
2043
- 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
+ }
2044
2037
  }
2045
- return new DataValue(dataValue);
2046
- }
2047
2038
 
2048
- // variation #3 :
2049
- function _Variable_bind_with_async_refresh(this: UAVariableImpl, options: any) {
2050
- /* jshint validthis: true */
2051
- assert(this instanceof UAVariableImpl);
2039
+ const _default_minimumSamplingInterval = 1000;
2052
2040
 
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");
2041
+ function coerceDataValue(dataValue: DataValue | DataValueLike): DataValue {
2042
+ if (dataValue instanceof DataValue) {
2043
+ return dataValue;
2044
+ }
2045
+ return new DataValue(dataValue);
2046
+ }
2056
2047
 
2057
- 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);
2058
2052
 
2059
- 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");
2060
2056
 
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);
2067
- }
2068
- }
2069
-
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);
2083
-
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
- );
2057
+ assert(!this.refreshFunc);
2058
+
2059
+ this.refreshFunc = options.refreshFunc;
2108
2060
 
2109
- 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);
2110
2067
  }
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 ");
2117
2068
  }
2118
- }
2119
-
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);
2129
2069
 
2130
- 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);
2131
2083
 
2132
- const timestamped_get_func_from__Variable_bind_with_simple_get = () => {
2133
- 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
+ );
2134
2108
 
2135
- /* istanbul ignore next */
2136
- if (!is_Variant_or_StatusCode(value)) {
2137
- errorLog(
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 !!!"
2144
- );
2145
- }
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 }));
2109
+ throw new Error(" Bind variable error: " + " the timestamped_get function must return a DataValue");
2151
2110
  }
2152
- 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 ");
2153
2117
  }
2154
- };
2155
-
2156
- _Variable_bind_with_timestamped_get.call(this, {
2157
- get: undefined,
2158
- timestamped_get: timestamped_get_func_from__Variable_bind_with_simple_get
2159
- });
2160
- }
2118
+ }
2161
2119
 
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");
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);
2166
2129
 
2167
- assert(!this._timestamped_set_func);
2168
- assert(!this._set_func);
2130
+ this._get_func = options.get;
2169
2131
 
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)");
2132
+ const timestamped_get_func_from__Variable_bind_with_simple_get = () => {
2133
+ const value: Variant | StatusCode = this._get_func();
2172
2134
 
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) {
2135
+ /* istanbul ignore next */
2136
+ if (!is_Variant_or_StatusCode(value)) {
2181
2137
  errorLog(
2182
- 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 !!!"
2183
2144
  );
2184
- errorLog(chalk.yellow("StatusCode.Good is assumed"));
2185
- return callback(err, StatusCodes.Good, timestamped_value);
2186
2145
  }
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
- ) {
2199
- assert(
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>{}"
2202
- );
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
- });
2235
- }
2236
- }
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
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
+
2251
2156
  _Variable_bind_with_timestamped_get.call(this, {
2252
2157
  get: undefined,
2253
- timestamped_get: options.timestamped_get
2158
+ timestamped_get: timestamped_get_func_from__Variable_bind_with_simple_get
2254
2159
  });
2255
- } else if (typeof options.refreshFunc === "function") {
2256
- // variation 3
2257
- _Variable_bind_with_async_refresh.call(this, options);
2258
- } 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
+ ) {
2259
2199
  assert(
2260
- !Object.prototype.hasOwnProperty.call(options, "set"),
2261
- "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>{}"
2262
2202
  );
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
- // }
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
+ });
2268
2235
  }
2269
2236
  }
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 { }
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 { }