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