node-opcua-address-space 2.87.0 → 2.88.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/LICENSE +1 -1
- package/dist/src/extension_object_array_node.d.ts +2 -40
- package/dist/src/extension_object_array_node.js +9 -45
- package/dist/src/extension_object_array_node.js.map +1 -1
- package/dist/src/idx_iterator.d.ts +8 -0
- package/dist/src/idx_iterator.js +51 -0
- package/dist/src/idx_iterator.js.map +1 -0
- package/dist/src/ua_reference_type_impl.d.ts +1 -1
- package/dist/src/ua_variable_impl.d.ts +11 -9
- package/dist/src/ua_variable_impl.js +23 -3
- package/dist/src/ua_variable_impl.js.map +1 -1
- package/dist/src/ua_variable_impl_ext_obj.d.ts +3 -1
- package/dist/src/ua_variable_impl_ext_obj.js +152 -20
- package/dist/src/ua_variable_impl_ext_obj.js.map +1 -1
- package/package.json +40 -40
- package/src/extension_object_array_node.ts +15 -54
- package/src/idx_iterator.ts +52 -0
- package/src/ua_variable_impl.ts +34 -13
- package/src/ua_variable_impl_ext_obj.ts +186 -33
|
@@ -23,7 +23,7 @@ const errorLog = make_errorLog(__filename);
|
|
|
23
23
|
*
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
-
function getExtObjArrayNodeValue<T
|
|
26
|
+
function getExtObjArrayNodeValue<T extends ExtensionObject>(this: UADynamicVariableArray<T>) {
|
|
27
27
|
return new Variant({
|
|
28
28
|
arrayType: VariantArrayType.Array,
|
|
29
29
|
dataType: DataType.ExtensionObject,
|
|
@@ -38,7 +38,7 @@ function removeElementByIndex<T extends ExtensionObject>(uaArrayVariableNode: UA
|
|
|
38
38
|
|
|
39
39
|
const addressSpace = uaArrayVariableNode.addressSpace;
|
|
40
40
|
const extObj = _array[elementIndex];
|
|
41
|
-
const browseName = uaArrayVariableNode.$$getElementBrowseName(extObj);
|
|
41
|
+
const browseName = uaArrayVariableNode.$$getElementBrowseName(extObj, elementIndex);
|
|
42
42
|
|
|
43
43
|
// remove element from global array (inefficient)
|
|
44
44
|
uaArrayVariableNode.$$extensionObjectArray.splice(elementIndex, 1);
|
|
@@ -68,14 +68,6 @@ function removeElementByIndex<T extends ExtensionObject>(uaArrayVariableNode: UA
|
|
|
68
68
|
/**
|
|
69
69
|
*
|
|
70
70
|
* create a node Variable that contains a array of ExtensionObject of a given type
|
|
71
|
-
* @method createExtObjArrayNode
|
|
72
|
-
* @param parentFolder
|
|
73
|
-
* @param options
|
|
74
|
-
* @param options.browseName
|
|
75
|
-
* @param options.complexVariableType
|
|
76
|
-
* @param options.variableType the type of Extension objects stored in the array.
|
|
77
|
-
* @param options.indexPropertyName
|
|
78
|
-
* @return {Object|UAVariable}
|
|
79
71
|
*/
|
|
80
72
|
export function createExtObjArrayNode<T extends ExtensionObject>(parentFolder: UAObject, options: any): UADynamicVariableArray<T> {
|
|
81
73
|
assert(typeof options.variableType === "string");
|
|
@@ -126,7 +118,9 @@ export function createExtObjArrayNode<T extends ExtensionObject>(parentFolder: U
|
|
|
126
118
|
|
|
127
119
|
return uaArrayVariableNode;
|
|
128
120
|
}
|
|
129
|
-
|
|
121
|
+
|
|
122
|
+
function _getElementBrowseName<T extends ExtensionObject>
|
|
123
|
+
(this: UADynamicVariableArray<T>, extObj: ExtensionObject, index: number | number[]) {
|
|
130
124
|
const indexPropertyName1 = this.$$indexPropertyName;
|
|
131
125
|
|
|
132
126
|
if (!Object.prototype.hasOwnProperty.call(extObj, indexPropertyName1)) {
|
|
@@ -137,13 +131,7 @@ function _getElementBrowseName<T extends ExtensionObject>(this: UADynamicVariabl
|
|
|
137
131
|
const browseName = (extObj as any)[indexPropertyName1].toString();
|
|
138
132
|
return browseName;
|
|
139
133
|
};
|
|
140
|
-
|
|
141
|
-
* @method bindExtObjArrayNode
|
|
142
|
-
* @param uaArrayVariableNode
|
|
143
|
-
* @param variableTypeNodeId
|
|
144
|
-
* @param indexPropertyName
|
|
145
|
-
* @return
|
|
146
|
-
*/
|
|
134
|
+
|
|
147
135
|
export function bindExtObjArrayNode<T extends ExtensionObject>(
|
|
148
136
|
uaArrayVariableNode: UADynamicVariableArray<T>,
|
|
149
137
|
variableTypeNodeId: string | NodeId,
|
|
@@ -155,26 +143,23 @@ export function bindExtObjArrayNode<T extends ExtensionObject>(
|
|
|
155
143
|
const addressSpace = uaArrayVariableNode.addressSpace;
|
|
156
144
|
|
|
157
145
|
const variableType = addressSpace.findVariableType(variableTypeNodeId);
|
|
158
|
-
|
|
159
146
|
// istanbul ignore next
|
|
160
147
|
if (!variableType || variableType.nodeId.isEmpty()) {
|
|
161
148
|
throw new Error("Cannot find VariableType " + variableTypeNodeId.toString());
|
|
162
149
|
}
|
|
163
150
|
|
|
164
151
|
const structure = addressSpace.findDataType("Structure");
|
|
165
|
-
|
|
166
152
|
// istanbul ignore next
|
|
167
153
|
if (!structure) {
|
|
168
154
|
throw new Error("Structure Type not found: please check your nodeset file");
|
|
169
155
|
}
|
|
170
156
|
|
|
171
157
|
let dataType = addressSpace.findDataType(variableType.dataType);
|
|
172
|
-
|
|
173
158
|
// istanbul ignore next
|
|
174
159
|
if (!dataType) {
|
|
175
160
|
throw new Error("Cannot find DataType " + variableType.dataType.toString());
|
|
176
161
|
}
|
|
177
|
-
|
|
162
|
+
|
|
178
163
|
assert(dataType.isSupertypeOf(structure), "expecting a structure (= ExtensionObject) here ");
|
|
179
164
|
|
|
180
165
|
assert(!uaArrayVariableNode.$$variableType, "uaArrayVariableNode has already been bound !");
|
|
@@ -197,7 +182,6 @@ export function bindExtObjArrayNode<T extends ExtensionObject>(
|
|
|
197
182
|
};
|
|
198
183
|
// bind the readonly
|
|
199
184
|
uaArrayVariableNode.bindVariable(bindOptions, true);
|
|
200
|
-
|
|
201
185
|
return uaArrayVariableNode;
|
|
202
186
|
}
|
|
203
187
|
|
|
@@ -208,20 +192,9 @@ export function bindExtObjArrayNode<T extends ExtensionObject>(
|
|
|
208
192
|
* @param uaArrayVariableNode {UAVariable}
|
|
209
193
|
* @return {UAVariable}
|
|
210
194
|
*
|
|
211
|
-
* @method addElement
|
|
212
|
-
* add a new element in a ExtensionObject Array variable
|
|
213
|
-
* @param nodeVariable a variable already exposing an extension objects
|
|
214
|
-
* @param uaArrayVariableNode {UAVariable}
|
|
215
|
-
* @return {UAVariable}
|
|
216
|
-
*
|
|
217
|
-
* @method addElement
|
|
218
|
-
* add a new element in a ExtensionObject Array variable
|
|
219
|
-
* @param constructor constructor of the extension object to create
|
|
220
|
-
* @param uaArrayVariableNode {UAVariable}
|
|
221
|
-
* @return {UAVariable}
|
|
222
195
|
*/
|
|
223
196
|
export function addElement<T extends ExtensionObject>(
|
|
224
|
-
options:
|
|
197
|
+
options: UAVariableImpl | ExtensionObject | Record<string, unknown>,
|
|
225
198
|
uaArrayVariableNode: UADynamicVariableArray<T>
|
|
226
199
|
): UAVariable {
|
|
227
200
|
assert(uaArrayVariableNode, " must provide an UAVariable containing the array");
|
|
@@ -256,19 +229,20 @@ export function addElement<T extends ExtensionObject>(
|
|
|
256
229
|
});
|
|
257
230
|
// xx elVar.bindExtensionObject();
|
|
258
231
|
} else {
|
|
259
|
-
if (options instanceof
|
|
232
|
+
if (options instanceof ExtensionObject) {
|
|
260
233
|
// extension object has already been created
|
|
261
234
|
extensionObject = options as T;
|
|
262
235
|
} else {
|
|
263
236
|
extensionObject = addressSpace.constructExtensionObject(uaArrayVariableNode.$$dataType, options) as T;
|
|
264
237
|
}
|
|
265
|
-
|
|
238
|
+
const index = uaArrayVariableNode.$$extensionObjectArray?.length || 0;
|
|
239
|
+
browseName = uaArrayVariableNode.$$getElementBrowseName(extensionObject, index);
|
|
266
240
|
elVar = uaArrayVariableNode.$$variableType.instantiate({
|
|
267
241
|
browseName,
|
|
268
242
|
componentOf: uaArrayVariableNode.nodeId,
|
|
269
243
|
value: { dataType: DataType.ExtensionObject, value: extensionObject }
|
|
270
244
|
}) as UAVariableImpl;
|
|
271
|
-
elVar.bindExtensionObject(extensionObject,
|
|
245
|
+
elVar.bindExtensionObject(extensionObject, { force: true });
|
|
272
246
|
elVar.$extensionObject = extensionObject;
|
|
273
247
|
}
|
|
274
248
|
|
|
@@ -279,19 +253,6 @@ export function addElement<T extends ExtensionObject>(
|
|
|
279
253
|
}
|
|
280
254
|
|
|
281
255
|
/**
|
|
282
|
-
*
|
|
283
|
-
* @method removeElement
|
|
284
|
-
* @param uaArrayVariableNode {UAVariable}
|
|
285
|
-
* @param element {number} index of element to remove in array
|
|
286
|
-
*
|
|
287
|
-
*
|
|
288
|
-
* @method removeElement
|
|
289
|
-
* @param uaArrayVariableNode {UAVariable}
|
|
290
|
-
* @param element {UAVariable} node of element to remove in array
|
|
291
|
-
*
|
|
292
|
-
* @method removeElement
|
|
293
|
-
* @param uaArrayVariableNode {UAVariable}
|
|
294
|
-
* @param element {ExtensionObject} extension object of the node of element to remove in array
|
|
295
256
|
*
|
|
296
257
|
*/
|
|
297
258
|
export function removeElement<T extends ExtensionObject>(
|
|
@@ -305,7 +266,7 @@ export function removeElement<T extends ExtensionObject>(
|
|
|
305
266
|
if (_array.length === 0) {
|
|
306
267
|
throw new Error(" cannot remove an element from an empty array ");
|
|
307
268
|
}
|
|
308
|
-
|
|
269
|
+
|
|
309
270
|
let elementIndex = -1;
|
|
310
271
|
|
|
311
272
|
if (typeof element === "number") {
|
|
@@ -316,7 +277,7 @@ export function removeElement<T extends ExtensionObject>(
|
|
|
316
277
|
// find element by name
|
|
317
278
|
const browseNameToFind = element.browseName.name!.toString();
|
|
318
279
|
elementIndex = _array.findIndex((obj: any, i: number) => {
|
|
319
|
-
const browseName = uaArrayVariableNode.$$getElementBrowseName(obj).toString();
|
|
280
|
+
const browseName = uaArrayVariableNode.$$getElementBrowseName(obj, elementIndex).toString();
|
|
320
281
|
return browseName === browseNameToFind;
|
|
321
282
|
});
|
|
322
283
|
} else if (typeof element === "function") {
|
|
@@ -327,7 +288,7 @@ export function removeElement<T extends ExtensionObject>(
|
|
|
327
288
|
assert(_array[0].constructor.name === (element as any).constructor.name, "element must match");
|
|
328
289
|
elementIndex = _array.findIndex((x: any) => x === element);
|
|
329
290
|
}
|
|
330
|
-
|
|
291
|
+
|
|
331
292
|
// istanbul ignore next
|
|
332
293
|
if (elementIndex < 0) {
|
|
333
294
|
throw new Error("removeElement: cannot find element matching " + element.toString());
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
export class IndexIterator {
|
|
6
|
+
|
|
7
|
+
public current: number[] | null = null;
|
|
8
|
+
constructor(private limits: number[]) {
|
|
9
|
+
this.reset();
|
|
10
|
+
}
|
|
11
|
+
public reset() {
|
|
12
|
+
this.current = [];
|
|
13
|
+
for (let i = 0; i < this.limits.length; i++) {
|
|
14
|
+
this.current[i] = 0;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
public increment() {
|
|
18
|
+
if (!this.current) return;
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
const increase = (n: number): boolean => {
|
|
22
|
+
if (n < 0) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
if (!this.current) return false;
|
|
26
|
+
if (this.current[n] + 1 >= this.limits[n]) {
|
|
27
|
+
if (n==0) {
|
|
28
|
+
this.current = null;
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
this.current[n] = 0;
|
|
32
|
+
return increase(n - 1);
|
|
33
|
+
}
|
|
34
|
+
this.current[n] = this.current[n] + 1;
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
const n = this.limits.length - 1;
|
|
38
|
+
if (!increase(n)) {
|
|
39
|
+
this.current = null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
public next(): number[] {
|
|
43
|
+
if (!this.current) {
|
|
44
|
+
throw new Error("Outof bond");
|
|
45
|
+
}
|
|
46
|
+
const r = [... this.current];
|
|
47
|
+
this.increment();
|
|
48
|
+
return r;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
}
|
package/src/ua_variable_impl.ts
CHANGED
|
@@ -28,7 +28,8 @@ import {
|
|
|
28
28
|
AccessLevelFlag,
|
|
29
29
|
makeAccessLevelFlag,
|
|
30
30
|
AttributeIds,
|
|
31
|
-
isDataEncoding
|
|
31
|
+
isDataEncoding,
|
|
32
|
+
QualifiedName
|
|
32
33
|
} from "node-opcua-data-model";
|
|
33
34
|
import { extractRange, sameDataValue, DataValue, DataValueLike, DataValueT } from "node-opcua-data-value";
|
|
34
35
|
import { coerceClock, getCurrentClock, PreciseClock } from "node-opcua-date-time";
|
|
@@ -87,6 +88,8 @@ import {
|
|
|
87
88
|
propagateTouchValueUpward,
|
|
88
89
|
setExtensionObjectValue,
|
|
89
90
|
_bindExtensionObject,
|
|
91
|
+
_bindExtensionObjectArray,
|
|
92
|
+
_bindExtensionObjectMatrix,
|
|
90
93
|
_installExtensionObjectBindingOnProperties,
|
|
91
94
|
_setExtensionObject,
|
|
92
95
|
_touchValue
|
|
@@ -1471,24 +1474,41 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
|
|
|
1471
1474
|
* @return {ExtensionObject}
|
|
1472
1475
|
*/
|
|
1473
1476
|
public bindExtensionObjectScalar(
|
|
1474
|
-
optionalExtensionObject
|
|
1477
|
+
optionalExtensionObject?: ExtensionObject,
|
|
1475
1478
|
options?: BindExtensionObjectOptions
|
|
1476
1479
|
): ExtensionObject | null {
|
|
1477
|
-
return this
|
|
1480
|
+
return _bindExtensionObject(this, optionalExtensionObject, options) as ExtensionObject;
|
|
1478
1481
|
}
|
|
1479
1482
|
|
|
1480
1483
|
public bindExtensionObjectArray(
|
|
1481
|
-
optionalExtensionObject
|
|
1484
|
+
optionalExtensionObject?: ExtensionObject[],
|
|
1482
1485
|
options?: BindExtensionObjectOptions
|
|
1483
1486
|
): ExtensionObject[] | null {
|
|
1484
|
-
|
|
1487
|
+
assert(this.valueRank === 1, "expecting a Array variable here");
|
|
1488
|
+
return _bindExtensionObjectArray(this, optionalExtensionObject, options) as ExtensionObject[];
|
|
1485
1489
|
}
|
|
1486
1490
|
|
|
1487
1491
|
public bindExtensionObject(
|
|
1488
1492
|
optionalExtensionObject?: ExtensionObject | ExtensionObject[],
|
|
1489
1493
|
options?: BindExtensionObjectOptions
|
|
1490
1494
|
): ExtensionObject | ExtensionObject[] | null {
|
|
1491
|
-
|
|
1495
|
+
if (optionalExtensionObject) {
|
|
1496
|
+
if (optionalExtensionObject instanceof Array) {
|
|
1497
|
+
return this.bindExtensionObjectArray(optionalExtensionObject, options);
|
|
1498
|
+
} else {
|
|
1499
|
+
return this.bindExtensionObjectScalar(optionalExtensionObject, options);
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
assert(optionalExtensionObject === undefined);
|
|
1503
|
+
if (this.valueRank === -1) {
|
|
1504
|
+
return this.bindExtensionObjectScalar(undefined, options);
|
|
1505
|
+
} else if (this.valueRank === 1) {
|
|
1506
|
+
return this.bindExtensionObjectArray(undefined, options);
|
|
1507
|
+
} else if (this.valueRank > 1) {
|
|
1508
|
+
return _bindExtensionObjectMatrix(this, undefined, options);
|
|
1509
|
+
}
|
|
1510
|
+
// unsupported case ...
|
|
1511
|
+
return null;
|
|
1492
1512
|
}
|
|
1493
1513
|
|
|
1494
1514
|
public updateExtensionObjectPartial(partialExtensionObject?: { [key: string]: any }): ExtensionObject {
|
|
@@ -1830,14 +1850,15 @@ UAVariableImpl.prototype.writeAttribute = thenify.withCallback(UAVariableImpl.pr
|
|
|
1830
1850
|
UAVariableImpl.prototype.historyRead = thenify.withCallback(UAVariableImpl.prototype.historyRead);
|
|
1831
1851
|
UAVariableImpl.prototype.readValueAsync = thenify.withCallback(UAVariableImpl.prototype.readValueAsync);
|
|
1832
1852
|
|
|
1833
|
-
export interface
|
|
1834
|
-
$$variableType?:
|
|
1835
|
-
$$dataType
|
|
1836
|
-
$$getElementBrowseName:
|
|
1837
|
-
$$extensionObjectArray:
|
|
1838
|
-
$$indexPropertyName:
|
|
1853
|
+
export interface UAVariableImplExtArray {
|
|
1854
|
+
$$variableType?: UAVariableType;
|
|
1855
|
+
$$dataType: UADataType;
|
|
1856
|
+
$$getElementBrowseName: (extObject: ExtensionObject, index: number | number[]) => QualifiedName;
|
|
1857
|
+
$$extensionObjectArray: ExtensionObject[];
|
|
1858
|
+
$$indexPropertyName: string;
|
|
1859
|
+
}
|
|
1860
|
+
export interface UAVariableImpl extends UAVariableImplExtArray {
|
|
1839
1861
|
}
|
|
1840
|
-
|
|
1841
1862
|
function check_valid_array(dataType: DataType, array: any): boolean {
|
|
1842
1863
|
if (Array.isArray(array)) {
|
|
1843
1864
|
return true;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import * as chalk from "chalk";
|
|
2
2
|
import assert from "node-opcua-assert";
|
|
3
|
-
import { BindExtensionObjectOptions, UADataType, UAVariable, UAVariableType } from "node-opcua-address-space-base";
|
|
4
|
-
import { NodeClass } from "node-opcua-data-model";
|
|
5
|
-
import { getCurrentClock, PreciseClock } from "node-opcua-date-time";
|
|
3
|
+
import { BindExtensionObjectOptions, UADataType, UADynamicVariableArray, UAVariable, UAVariableType } from "node-opcua-address-space-base";
|
|
4
|
+
import { coerceQualifiedName, NodeClass } from "node-opcua-data-model";
|
|
5
|
+
import { getCurrentClock, PreciseClock, DateWithPicoseconds, coerceClock } from "node-opcua-date-time";
|
|
6
6
|
import { DataValue } from "node-opcua-data-value";
|
|
7
7
|
import { make_debugLog, make_warningLog, checkDebugFlag, make_errorLog } from "node-opcua-debug";
|
|
8
8
|
import { ExtensionObject } from "node-opcua-extension-object";
|
|
9
|
-
import { NodeId } from "node-opcua-nodeid";
|
|
9
|
+
import { coerceNodeId, NodeId, NodeIdType } from "node-opcua-nodeid";
|
|
10
10
|
import { StatusCodes, CallbackT, StatusCode } from "node-opcua-status-code";
|
|
11
11
|
import { StructureField } from "node-opcua-types";
|
|
12
12
|
import { lowerFirstLetter } from "node-opcua-utils";
|
|
@@ -15,6 +15,9 @@ import { DataType, Variant, VariantLike, VariantArrayType } from "node-opcua-var
|
|
|
15
15
|
import { valueRankToString } from "./base_node_private";
|
|
16
16
|
import { UAVariableImpl } from "./ua_variable_impl";
|
|
17
17
|
import { UADataTypeImpl } from "./ua_data_type_impl";
|
|
18
|
+
import { bindExtObjArrayNode } from "./extension_object_array_node";
|
|
19
|
+
import { IndexIterator } from "./idx_iterator";
|
|
20
|
+
import { DateTime } from "node-opcua-basic-types";
|
|
18
21
|
|
|
19
22
|
const doDebug = checkDebugFlag(__filename);
|
|
20
23
|
const debugLog = make_debugLog(__filename);
|
|
@@ -114,7 +117,7 @@ function propagateTouchValueDownward(self: UAVariableImpl, now: PreciseClock): v
|
|
|
114
117
|
}
|
|
115
118
|
}
|
|
116
119
|
|
|
117
|
-
export function _setExtensionObject(self: UAVariableImpl, ext: ExtensionObject | ExtensionObject[]): void {
|
|
120
|
+
export function _setExtensionObject(self: UAVariableImpl, ext: ExtensionObject | ExtensionObject[], sourceTimestamp?: PreciseClock): void {
|
|
118
121
|
// assert(!(ext as any).$isProxy, "internal error ! ExtensionObject has already been proxied !");
|
|
119
122
|
if (Array.isArray(ext)) {
|
|
120
123
|
assert(self.valueRank === 1, "Only Array is supported for the time being");
|
|
@@ -133,7 +136,7 @@ export function _setExtensionObject(self: UAVariableImpl, ext: ExtensionObject |
|
|
|
133
136
|
self.$dataValue.statusCode = StatusCodes.Good;
|
|
134
137
|
}
|
|
135
138
|
|
|
136
|
-
const now = getCurrentClock();
|
|
139
|
+
const now = sourceTimestamp || getCurrentClock();
|
|
137
140
|
propagateTouchValueUpward(self, now);
|
|
138
141
|
propagateTouchValueDownward(self, now);
|
|
139
142
|
}
|
|
@@ -193,7 +196,10 @@ function getOrCreateProperty(
|
|
|
193
196
|
browseName: { namespaceIndex: structureNamespace, name: field.name!.toString() },
|
|
194
197
|
componentOf: variableNode,
|
|
195
198
|
dataType: field.dataType,
|
|
196
|
-
minimumSamplingInterval: variableNode.minimumSamplingInterval
|
|
199
|
+
minimumSamplingInterval: variableNode.minimumSamplingInterval,
|
|
200
|
+
accessLevel: variableNode.accessLevel,
|
|
201
|
+
accessRestrictions: variableNode.accessRestrictions,
|
|
202
|
+
rolePermissions: variableNode.rolePermissions
|
|
197
203
|
}) as UAVariableImpl;
|
|
198
204
|
assert(property.minimumSamplingInterval === variableNode.minimumSamplingInterval);
|
|
199
205
|
}
|
|
@@ -226,17 +232,46 @@ function bindProperty(variableNode: UAVariableImpl, propertyNode: UAVariableImpl
|
|
|
226
232
|
return new DataValue(propertyNode.$dataValue);
|
|
227
233
|
},
|
|
228
234
|
timestamped_set: (_dataValue: DataValue, callback: CallbackT<StatusCode>) => {
|
|
229
|
-
|
|
235
|
+
|
|
236
|
+
propertyNode.setValueFromSource(_dataValue.value, _dataValue.statusCode, _dataValue.sourceTimestamp || new Date());
|
|
237
|
+
// callback(null, StatusCodes.BadNotWritable);
|
|
238
|
+
callback(null, StatusCodes.Good);
|
|
230
239
|
}
|
|
231
240
|
},
|
|
232
241
|
true
|
|
233
242
|
);
|
|
234
243
|
}
|
|
235
244
|
|
|
245
|
+
|
|
246
|
+
function _initial_setup(variableNode: UAVariableImpl) {
|
|
247
|
+
const dataValue = variableNode.readValue();
|
|
248
|
+
const extObj = dataValue.value.value;
|
|
249
|
+
if (extObj instanceof ExtensionObject) {
|
|
250
|
+
variableNode.bindExtensionObject(extObj, { createMissingProp: true, force: true });
|
|
251
|
+
} else if (extObj instanceof Array) {
|
|
252
|
+
if (dataValue.value.arrayType === VariantArrayType.Array) {
|
|
253
|
+
variableNode.bindExtensionObjectArray(extObj, { createMissingProp: true, force: true });
|
|
254
|
+
} else if (dataValue.value.arrayType === VariantArrayType.Matrix) {
|
|
255
|
+
_bindExtensionObjectMatrix(variableNode, extObj, { createMissingProp: true, force: true });
|
|
256
|
+
} else {
|
|
257
|
+
throw new Error("Internal Error, unexpected case");
|
|
258
|
+
}
|
|
259
|
+
} else {
|
|
260
|
+
const msg = `variableNode ${variableNode.browseName.toString()} doesn't have $extensionObject property`;
|
|
261
|
+
errorLog(msg);
|
|
262
|
+
errorLog(dataValue.toString());
|
|
263
|
+
throw new Error(msg);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
236
266
|
export function _installExtensionObjectBindingOnProperties(
|
|
237
267
|
variableNode: UAVariableImpl,
|
|
238
268
|
options: BindExtensionObjectOptions
|
|
239
269
|
): void {
|
|
270
|
+
|
|
271
|
+
if (!variableNode.$extensionObject) {
|
|
272
|
+
_initial_setup(variableNode);
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
240
275
|
const addressSpace = variableNode.addressSpace;
|
|
241
276
|
const dt = variableNode.getDataTypeNode();
|
|
242
277
|
const definition = dt.getStructureDefinition();
|
|
@@ -256,7 +291,12 @@ export function _installExtensionObjectBindingOnProperties(
|
|
|
256
291
|
continue;
|
|
257
292
|
}
|
|
258
293
|
propertyNode.$dataValue.statusCode = StatusCodes.Good;
|
|
259
|
-
propertyNode.
|
|
294
|
+
propertyNode.$dataValue.sourceTimestamp = variableNode.$dataValue.sourceTimestamp;
|
|
295
|
+
propertyNode.$dataValue.sourcePicoseconds = variableNode.$dataValue.sourcePicoseconds;
|
|
296
|
+
propertyNode.$dataValue.serverTimestamp = variableNode.$dataValue.serverTimestamp;
|
|
297
|
+
propertyNode.$dataValue.serverPicoseconds = variableNode.$dataValue.serverPicoseconds;
|
|
298
|
+
|
|
299
|
+
//xx propertyNode.touchValue();
|
|
260
300
|
|
|
261
301
|
const basicDataType = addressSpace.findCorrespondingBasicDataType(field.dataType);
|
|
262
302
|
|
|
@@ -322,36 +362,43 @@ export function _installExtensionObjectBindingOnProperties(
|
|
|
322
362
|
}
|
|
323
363
|
}
|
|
324
364
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
self: UAVariableImpl,
|
|
328
|
-
optionalExtensionObject?: ExtensionObject | ExtensionObject[],
|
|
329
|
-
options?: BindExtensionObjectOptions
|
|
330
|
-
): ExtensionObject | ExtensionObject[] | null {
|
|
331
|
-
options = options || { createMissingProp: false };
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
const addressSpace = self.addressSpace;
|
|
365
|
+
function isVariableContainingExtensionObject(uaVariable: UAVariableImpl): boolean {
|
|
366
|
+
const addressSpace = uaVariable.addressSpace;
|
|
335
367
|
const structure = addressSpace.findDataType("Structure");
|
|
336
|
-
let extensionObject_;
|
|
337
368
|
|
|
338
369
|
if (!structure) {
|
|
339
370
|
// the addressSpace is limited and doesn't provide extension object
|
|
340
371
|
// bindExtensionObject cannot be performed and shall finish here.
|
|
341
|
-
return
|
|
372
|
+
return false;
|
|
342
373
|
}
|
|
343
374
|
|
|
344
375
|
assert(structure.browseName.toString() === "Structure", "expecting DataType Structure to be in IAddressSpace");
|
|
345
376
|
|
|
346
|
-
const dt =
|
|
377
|
+
const dt = uaVariable.getDataTypeNode() as UADataTypeImpl;
|
|
347
378
|
if (!dt.isSupertypeOf(structure)) {
|
|
379
|
+
return false;
|
|
380
|
+
}
|
|
381
|
+
return true;
|
|
382
|
+
|
|
383
|
+
}
|
|
384
|
+
// eslint-disable-next-line complexity
|
|
385
|
+
export function _bindExtensionObject(
|
|
386
|
+
self: UAVariableImpl,
|
|
387
|
+
optionalExtensionObject?: ExtensionObject | ExtensionObject[],
|
|
388
|
+
options?: BindExtensionObjectOptions
|
|
389
|
+
): ExtensionObject | ExtensionObject[] | null {
|
|
390
|
+
options = options || { createMissingProp: false };
|
|
391
|
+
|
|
392
|
+
if (!isVariableContainingExtensionObject(self)) {
|
|
348
393
|
return null;
|
|
349
394
|
}
|
|
395
|
+
const addressSpace = self.addressSpace;
|
|
396
|
+
let extensionObject_;
|
|
350
397
|
|
|
351
|
-
if (self.valueRank !== -1 && self.valueRank !==1) {
|
|
398
|
+
if (self.valueRank !== -1 && self.valueRank !== 1) {
|
|
352
399
|
throw new Error("Cannot bind an extension object here, valueRank must be scalar (-1) or one-dimensional (1)");
|
|
353
400
|
}
|
|
354
|
-
|
|
401
|
+
|
|
355
402
|
// istanbul ignore next
|
|
356
403
|
if (doDebug) {
|
|
357
404
|
debugLog(" ------------------------------ binding ", self.browseName.toString(), self.nodeId.toString());
|
|
@@ -365,6 +412,7 @@ export function _bindExtensionObject(
|
|
|
365
412
|
) {
|
|
366
413
|
const parentDataType = (self.parent as UAVariable | UAVariableType).dataType;
|
|
367
414
|
const dataTypeNode = addressSpace.findNode(parentDataType) as UADataType;
|
|
415
|
+
const structure = addressSpace.findDataType("Structure")!;
|
|
368
416
|
// istanbul ignore next
|
|
369
417
|
if (dataTypeNode && dataTypeNode.isSupertypeOf(structure)) {
|
|
370
418
|
// warningLog(
|
|
@@ -392,9 +440,9 @@ export function _bindExtensionObject(
|
|
|
392
440
|
warningLog(self.$extensionObject?.toString());
|
|
393
441
|
throw new Error(
|
|
394
442
|
"bindExtensionObject: $extensionObject is incorrect: we are expecting a " +
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
443
|
+
self.dataType.toString({ addressSpace: self.addressSpace }) +
|
|
444
|
+
" but we got a " +
|
|
445
|
+
self.$extensionObject?.constructor.name
|
|
398
446
|
);
|
|
399
447
|
}
|
|
400
448
|
return self.$extensionObject;
|
|
@@ -422,6 +470,10 @@ export function _bindExtensionObject(
|
|
|
422
470
|
return self.$extensionObject;
|
|
423
471
|
|
|
424
472
|
function innerBindExtensionObject() {
|
|
473
|
+
const makePreciseClock = (sourceTimestamp: DateTime, picoseconds?: number): PreciseClock =>
|
|
474
|
+
({ timestamp: sourceTimestamp as DateWithPicoseconds, picoseconds: picoseconds || 0 });
|
|
475
|
+
|
|
476
|
+
|
|
425
477
|
if (s.value && (s.value.dataType === DataType.Null || (s.value.dataType === DataType.ExtensionObject && !s.value.value))) {
|
|
426
478
|
if (self.valueRank === -1 /** Scalar */) {
|
|
427
479
|
// create a structure and bind it
|
|
@@ -439,13 +491,16 @@ export function _bindExtensionObject(
|
|
|
439
491
|
if (!self.checkExtensionObjectIsCorrect(ext)) {
|
|
440
492
|
return callback(null, StatusCodes.BadInvalidArgument);
|
|
441
493
|
}
|
|
442
|
-
|
|
494
|
+
const sourceTime = coerceClock(dataValue.sourceTimestamp, dataValue.sourcePicoseconds);
|
|
495
|
+
_setExtensionObject(self, ext, sourceTime);
|
|
443
496
|
callback(null, StatusCodes.Good);
|
|
444
497
|
}
|
|
445
498
|
},
|
|
446
499
|
true
|
|
447
500
|
);
|
|
448
|
-
|
|
501
|
+
const sourceTime = coerceClock(self.$dataValue.sourceTimestamp, self.$dataValue.sourcePicoseconds);
|
|
502
|
+
_setExtensionObject(self, extensionObject_, sourceTime);
|
|
503
|
+
|
|
449
504
|
} else if (self.valueRank === 1 /** Array */) {
|
|
450
505
|
// create a structure and bind it
|
|
451
506
|
|
|
@@ -462,17 +517,19 @@ export function _bindExtensionObject(
|
|
|
462
517
|
return d;
|
|
463
518
|
},
|
|
464
519
|
timestamped_set(dataValue: DataValue, callback: CallbackT<StatusCode>) {
|
|
465
|
-
const
|
|
466
|
-
if (!self.checkExtensionObjectIsCorrect(
|
|
520
|
+
const extensionObjectArray = dataValue.value.value;
|
|
521
|
+
if (!self.checkExtensionObjectIsCorrect(extensionObjectArray)) {
|
|
467
522
|
return callback(null, StatusCodes.BadInvalidArgument);
|
|
468
523
|
}
|
|
469
|
-
|
|
524
|
+
const sourceTime = coerceClock(dataValue.sourceTimestamp, dataValue.sourcePicoseconds);
|
|
525
|
+
_setExtensionObject(self, extensionObjectArray, sourceTime);
|
|
470
526
|
callback(null, StatusCodes.Good);
|
|
471
527
|
}
|
|
472
528
|
},
|
|
473
529
|
true
|
|
474
530
|
);
|
|
475
|
-
|
|
531
|
+
const sourceTime = coerceClock(self.$dataValue.sourceTimestamp, self.$dataValue.sourcePicoseconds);
|
|
532
|
+
_setExtensionObject(self, extensionObject_, sourceTime);
|
|
476
533
|
} else {
|
|
477
534
|
errorLog(self.toString());
|
|
478
535
|
errorLog("Unsupported case ! valueRank= ", self.valueRank);
|
|
@@ -488,6 +545,102 @@ export function _bindExtensionObject(
|
|
|
488
545
|
}
|
|
489
546
|
}
|
|
490
547
|
|
|
548
|
+
const getIndexAsText = (index: number | number[]): string => {
|
|
549
|
+
if (typeof index === "number")
|
|
550
|
+
return `${index}`;
|
|
551
|
+
return `${index.map((a) => a.toString()).join(",")}`;
|
|
552
|
+
}
|
|
553
|
+
const composeBrowseNameAndNodeId = (uaVariable: UAVariable, indexes: number[]) => {
|
|
554
|
+
const iAsText = getIndexAsText(indexes);
|
|
555
|
+
const browseName = coerceQualifiedName(iAsText);
|
|
556
|
+
let nodeId: NodeId | undefined;
|
|
557
|
+
if (uaVariable.nodeId.identifierType === NodeIdType.STRING) {
|
|
558
|
+
nodeId = new NodeId(NodeIdType.STRING, uaVariable.nodeId.value as string + `[${iAsText}]`, uaVariable.nodeId.namespace);
|
|
559
|
+
}
|
|
560
|
+
return { browseName, nodeId };
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
export function _bindExtensionObjectArray(
|
|
564
|
+
uaVariable: UAVariableImpl,
|
|
565
|
+
optionalExtensionObjectArray?: ExtensionObject[],
|
|
566
|
+
options?: BindExtensionObjectOptions
|
|
567
|
+
): ExtensionObject[] {
|
|
568
|
+
return _bindExtensionObjectMatrix(uaVariable, optionalExtensionObjectArray, options);
|
|
569
|
+
}
|
|
570
|
+
export function _bindExtensionObjectMatrix(
|
|
571
|
+
uaVariable: UAVariableImpl,
|
|
572
|
+
optionalExtensionObjectArray?: ExtensionObject[],
|
|
573
|
+
options?: BindExtensionObjectOptions
|
|
574
|
+
): ExtensionObject[] {
|
|
575
|
+
|
|
576
|
+
if (uaVariable.valueRank < 1) {
|
|
577
|
+
throw new Error("Variable must be a MultiDimensional array");
|
|
578
|
+
}
|
|
579
|
+
const arrayDimensions = uaVariable.arrayDimensions || [];
|
|
580
|
+
|
|
581
|
+
if (!isVariableContainingExtensionObject(uaVariable)) {
|
|
582
|
+
return [];
|
|
583
|
+
}
|
|
584
|
+
if ((arrayDimensions.length === 0 || arrayDimensions.length === 1 && arrayDimensions[0] === 0) && optionalExtensionObjectArray) {
|
|
585
|
+
arrayDimensions[0] = optionalExtensionObjectArray.length;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
const totalLength = arrayDimensions.reduce((p, c) => p * c, 1);
|
|
589
|
+
|
|
590
|
+
/** */
|
|
591
|
+
const addressSpace = uaVariable.addressSpace;
|
|
592
|
+
if (optionalExtensionObjectArray) {
|
|
593
|
+
if (optionalExtensionObjectArray.length !== totalLength) {
|
|
594
|
+
throw new Error(`optionalExtensionObjectArray must have the expected number of element matching ${arrayDimensions}`)
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
if (!optionalExtensionObjectArray) {
|
|
598
|
+
optionalExtensionObjectArray = [];
|
|
599
|
+
for (let i = 0; i < totalLength; i++) {
|
|
600
|
+
optionalExtensionObjectArray[i] = addressSpace.constructExtensionObject(uaVariable.dataType, {});;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
uaVariable.$$extensionObjectArray = optionalExtensionObjectArray;
|
|
604
|
+
|
|
605
|
+
// uaVariable.$$getElementBrowseName = getElementBrowseNameByIndex;
|
|
606
|
+
|
|
607
|
+
uaVariable.bindVariable({
|
|
608
|
+
get: () => new Variant({
|
|
609
|
+
arrayType: VariantArrayType.Array,
|
|
610
|
+
dataType: DataType.ExtensionObject,
|
|
611
|
+
value: uaVariable.$$extensionObjectArray
|
|
612
|
+
})
|
|
613
|
+
}, true);
|
|
614
|
+
const namespace = uaVariable.namespace;
|
|
615
|
+
const indexIterator = new IndexIterator(arrayDimensions);
|
|
616
|
+
for (let i = 0; i < totalLength; i++) {
|
|
617
|
+
|
|
618
|
+
const index = indexIterator.next();
|
|
619
|
+
|
|
620
|
+
const { browseName, nodeId } = composeBrowseNameAndNodeId(uaVariable, index);
|
|
621
|
+
|
|
622
|
+
const uaElement = namespace.addVariable({
|
|
623
|
+
browseName,
|
|
624
|
+
nodeId,
|
|
625
|
+
componentOf: uaVariable,
|
|
626
|
+
dataType: uaVariable.dataType,
|
|
627
|
+
valueRank: -1,
|
|
628
|
+
accessLevel: uaVariable.userAccessLevel
|
|
629
|
+
});
|
|
630
|
+
{
|
|
631
|
+
const capturedIndex = i;
|
|
632
|
+
uaElement.bindVariable({
|
|
633
|
+
get: () => new Variant({
|
|
634
|
+
dataType: DataType.ExtensionObject,
|
|
635
|
+
arrayType: VariantArrayType.Scalar,
|
|
636
|
+
value: uaVariable.$$extensionObjectArray[capturedIndex]
|
|
637
|
+
})
|
|
638
|
+
})
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
return uaVariable.$$extensionObjectArray;
|
|
642
|
+
}
|
|
643
|
+
|
|
491
644
|
export function extractPartialData(path: string | string[], extensionObject: ExtensionObject) {
|
|
492
645
|
let name;
|
|
493
646
|
if (typeof path === "string") {
|