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