node-opcua-address-space 2.187.8 → 2.187.11
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/impl/base_node_private.d.ts.map +1 -1
- package/dist/impl/base_node_private.js +7 -4
- package/dist/impl/base_node_private.js.map +1 -1
- package/dist/impl/ua_variable_impl.d.ts +5 -3
- package/dist/impl/ua_variable_impl.d.ts.map +1 -1
- package/dist/impl/ua_variable_impl.js +60 -34
- package/dist/impl/ua_variable_impl.js.map +1 -1
- package/dist/tsconfig_base.tsbuildinfo +1 -1
- package/impl/base_node_private.ts +7 -4
- package/impl/ua_variable_impl.ts +73 -41
- package/package.json +16 -16
|
@@ -811,7 +811,7 @@ function _clone_collection_new(
|
|
|
811
811
|
|
|
812
812
|
// also clone or instantiate interface members that may be required in the optionals
|
|
813
813
|
extraInfo.level++;
|
|
814
|
-
_cloneInterface(nodeToClone,
|
|
814
|
+
_cloneInterface(nodeToClone, clonedNode, node, optionalFilter, extraInfo, browseNameMap);
|
|
815
815
|
extraInfo.level--;
|
|
816
816
|
// === clone);
|
|
817
817
|
// extraInfo.registerClonedObject(clone, node);
|
|
@@ -898,7 +898,7 @@ function _extractInterfaces2(typeDefinitionNode: UAObjectType | UAVariableType,
|
|
|
898
898
|
|
|
899
899
|
function _cloneInterface(
|
|
900
900
|
nodeToClone: UAObject | UAVariable | UAMethod | UAObjectType | UAVariableType,
|
|
901
|
-
|
|
901
|
+
clonedNode: BaseNode,
|
|
902
902
|
node: UAObject | UAVariable | UAMethod,
|
|
903
903
|
optionalFilter: CloneFilter,
|
|
904
904
|
extraInfo: CloneExtraInfo,
|
|
@@ -936,7 +936,10 @@ function _cloneInterface(
|
|
|
936
936
|
const localFilter = optionalFilter.filterFor(node);
|
|
937
937
|
|
|
938
938
|
for (const iface of interfaces) {
|
|
939
|
-
|
|
939
|
+
// a member the clone already has (the type declares it itself) is not cloned a second time
|
|
940
|
+
const aggregates = iface
|
|
941
|
+
.findReferencesEx("Aggregates", BrowseDirection.Forward)
|
|
942
|
+
.filter((r) => !r.node || !clonedNode.getChildByName(r.node.browseName));
|
|
940
943
|
if (aggregates.length === 0) continue;
|
|
941
944
|
// c8 ignore next
|
|
942
945
|
doTrace &&
|
|
@@ -946,7 +949,7 @@ function _cloneInterface(
|
|
|
946
949
|
`\n${extraInfo?.pad()}`,
|
|
947
950
|
aggregates.map((r) => r.toString({ addressSpace })).join(`\n${extraInfo?.pad()}`)
|
|
948
951
|
);
|
|
949
|
-
_clone_collection_new(nodeToClone,
|
|
952
|
+
_clone_collection_new(nodeToClone, clonedNode, aggregates, false, localFilter, extraInfo, browseNameMap);
|
|
950
953
|
}
|
|
951
954
|
}
|
|
952
955
|
|
package/impl/ua_variable_impl.ts
CHANGED
|
@@ -255,7 +255,12 @@ export class UAVariableImpl<T extends UAVariableEvents & ListenerSignature<T> =
|
|
|
255
255
|
* it, as the Read service does for the others, evaluates the getter twice for nothing.
|
|
256
256
|
*/
|
|
257
257
|
declare public _refreshFuncWrapsGetter?: boolean;
|
|
258
|
-
|
|
258
|
+
// the read that started the refresh in progress, and the reads that came while it was (an array only then):
|
|
259
|
+
// a sampled variable is read once per sample, and one at a time
|
|
260
|
+
declare private __waiting_callback?: CallbackT<DataValue>;
|
|
261
|
+
declare private __more_waiting_callbacks?: CallbackT<DataValue>[];
|
|
262
|
+
// set by _readValueAsync for the next asyncRefresh when its clock is shared by a sampler (see asyncRefresh)
|
|
263
|
+
declare private __sharedReadClock?: boolean;
|
|
259
264
|
// the variant types already found to fit the DataType below (bit k: DataType k), set on the first write
|
|
260
265
|
declare private _validatedForDataType?: NodeId;
|
|
261
266
|
declare private _validatedVariantTypes?: number;
|
|
@@ -553,40 +558,35 @@ export class UAVariableImpl<T extends UAVariableEvents & ListenerSignature<T> =
|
|
|
553
558
|
this.verifyVariantCompatibility(this.$dataValue.value);
|
|
554
559
|
}
|
|
555
560
|
|
|
556
|
-
const lessOrEqual = (a: PreciseClock, b: PreciseClock) => {
|
|
557
|
-
return (
|
|
558
|
-
a.timestamp.getTime() < b.timestamp.getTime() ||
|
|
559
|
-
(a.timestamp.getTime() === b.timestamp.getTime() && a.picoseconds <= b.picoseconds)
|
|
560
|
-
);
|
|
561
|
-
};
|
|
562
|
-
|
|
563
561
|
if (!this.refreshFunc) {
|
|
564
562
|
// no refresh func
|
|
565
563
|
const dataValue = this.readValue();
|
|
566
564
|
dataValue.serverTimestamp = oldestDate.timestamp;
|
|
567
565
|
dataValue.serverPicoseconds = oldestDate.picoseconds;
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
cb(null, dataValue);
|
|
571
|
-
return;
|
|
572
|
-
} else {
|
|
573
|
-
// fake
|
|
574
|
-
cb(null, dataValue);
|
|
575
|
-
return;
|
|
576
|
-
}
|
|
566
|
+
cb(null, dataValue);
|
|
567
|
+
return;
|
|
577
568
|
}
|
|
578
569
|
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
if (c1 && lessOrEqual(oldestDate, c1)) {
|
|
570
|
+
// the value held is recent enough for this read: it is answered without a refresh
|
|
571
|
+
const held = this.$dataValue;
|
|
572
|
+
if (held.serverTimestamp && clockNotAfter(oldestDate, held.serverTimestamp, held.serverPicoseconds)) {
|
|
584
573
|
const dataValue = this.readValue().clone();
|
|
585
574
|
dataValue.serverTimestamp = oldestDate.timestamp;
|
|
586
575
|
dataValue.serverPicoseconds = oldestDate.picoseconds;
|
|
587
576
|
cb(null, dataValue);
|
|
588
577
|
return;
|
|
589
578
|
}
|
|
579
|
+
// a refreshed value without a server timestamp is stamped with the time the server obtained it
|
|
580
|
+
// (OPC 10000-4 7.11), read from the clock once the refreshFunc has answered, so it is never earlier
|
|
581
|
+
// than a source timestamp the refreshFunc took. Left unset, it inherited the previous value's server
|
|
582
|
+
// timestamp, which never moved on. A sampler that reads many variables in one go shares one clock
|
|
583
|
+
// read between them: their values get that clock's Date (the sampler recognises it by identity and
|
|
584
|
+
// stamps them all with one clock read made once they have all answered).
|
|
585
|
+
const sharedClock = this.__sharedReadClock === true;
|
|
586
|
+
this.__sharedReadClock = false;
|
|
587
|
+
const requestedAt = oldestDate.timestamp;
|
|
588
|
+
const requestedAtPicoseconds = oldestDate.picoseconds;
|
|
589
|
+
let answeredAtOnce = true;
|
|
590
590
|
try {
|
|
591
591
|
this.refreshFunc.call(this, (err: Error | null, dataValue?: DataValueLike) => {
|
|
592
592
|
// c8 ignore next
|
|
@@ -600,10 +600,22 @@ export class UAVariableImpl<T extends UAVariableEvents & ListenerSignature<T> =
|
|
|
600
600
|
dataValue = { statusCode: StatusCodes.BadNoDataAvailable };
|
|
601
601
|
}
|
|
602
602
|
if (dataValue && dataValue !== this.$dataValue) {
|
|
603
|
-
|
|
603
|
+
const refreshed = coerceDataValue(dataValue);
|
|
604
|
+
if (!refreshed.serverTimestamp) {
|
|
605
|
+
if (answeredAtOnce && sharedClock) {
|
|
606
|
+
refreshed.serverTimestamp = requestedAt;
|
|
607
|
+
refreshed.serverPicoseconds = requestedAtPicoseconds;
|
|
608
|
+
} else {
|
|
609
|
+
const now = getCurrentClock();
|
|
610
|
+
refreshed.serverTimestamp = now.timestamp;
|
|
611
|
+
refreshed.serverPicoseconds = now.picoseconds;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
this._internal_set_dataValue(refreshed, null);
|
|
604
615
|
}
|
|
605
616
|
cb(err, this.$dataValue);
|
|
606
617
|
});
|
|
618
|
+
answeredAtOnce = false;
|
|
607
619
|
} catch (err) {
|
|
608
620
|
errorLog("-------------- refresh call failed 2", this.browseName.toString(), this.nodeId.toString());
|
|
609
621
|
errorLog(err);
|
|
@@ -1314,13 +1326,17 @@ export class UAVariableImpl<T extends UAVariableEvents & ListenerSignature<T> =
|
|
|
1314
1326
|
/**
|
|
1315
1327
|
|
|
1316
1328
|
*/
|
|
1317
|
-
public readValueAsync(context: ISessionContext | null, callback: CallbackT<DataValue
|
|
1329
|
+
public readValueAsync(context: ISessionContext | null, callback: CallbackT<DataValue>, clock?: PreciseClock): void;
|
|
1318
1330
|
public readValueAsync(context: ISessionContext | null): Promise<DataValue>;
|
|
1319
|
-
public readValueAsync(
|
|
1331
|
+
public readValueAsync(
|
|
1332
|
+
context: ISessionContext | null,
|
|
1333
|
+
callback?: CallbackT<DataValue>,
|
|
1334
|
+
clock?: PreciseClock
|
|
1335
|
+
): Promise<DataValue> | undefined {
|
|
1320
1336
|
// written out rather than wrapped with thenify-ex: every sample of a monitored item comes
|
|
1321
1337
|
// through here with a callback, and the generic wrapper forwarded it with apply(arguments)
|
|
1322
1338
|
if (callback) {
|
|
1323
|
-
this._readValueAsync(context, callback);
|
|
1339
|
+
this._readValueAsync(context, callback, clock);
|
|
1324
1340
|
return undefined;
|
|
1325
1341
|
}
|
|
1326
1342
|
return new Promise<DataValue>((resolve, reject) => {
|
|
@@ -1328,14 +1344,13 @@ export class UAVariableImpl<T extends UAVariableEvents & ListenerSignature<T> =
|
|
|
1328
1344
|
});
|
|
1329
1345
|
}
|
|
1330
1346
|
|
|
1331
|
-
private _readValueAsync(context: ISessionContext | null, callback: CallbackT<DataValue
|
|
1347
|
+
private _readValueAsync(context: ISessionContext | null, callback: CallbackT<DataValue>, readAt?: PreciseClock): void {
|
|
1332
1348
|
context = context || SessionContext.defaultContext;
|
|
1333
1349
|
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
if (_readValueAsync_in_progress) {
|
|
1350
|
+
if (this.__waiting_callback) {
|
|
1351
|
+
// a refresh is in progress: its value answers this read too
|
|
1352
|
+
this.__more_waiting_callbacks ??= [];
|
|
1353
|
+
this.__more_waiting_callbacks.push(callback);
|
|
1339
1354
|
return;
|
|
1340
1355
|
}
|
|
1341
1356
|
|
|
@@ -1343,6 +1358,7 @@ export class UAVariableImpl<T extends UAVariableEvents & ListenerSignature<T> =
|
|
|
1343
1358
|
callback?.(null, new DataValue({ statusCode: StatusCodes.BadNodeIdUnknown }));
|
|
1344
1359
|
return;
|
|
1345
1360
|
}
|
|
1361
|
+
this.__waiting_callback = callback;
|
|
1346
1362
|
|
|
1347
1363
|
// runs once per sample of every monitored item, so nothing here allocates a closure or
|
|
1348
1364
|
// binds a function: the denial (if any) is a StatusCode, and the callback that releases
|
|
@@ -1371,11 +1387,16 @@ export class UAVariableImpl<T extends UAVariableEvents & ListenerSignature<T> =
|
|
|
1371
1387
|
}
|
|
1372
1388
|
|
|
1373
1389
|
this.__satisfy_waiting_callbacks ??= (err: Error | null, dataValue?: DataValue) => {
|
|
1374
|
-
// now call all pending callbacks
|
|
1375
|
-
const
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1390
|
+
// now call all pending callbacks, in the order of the reads
|
|
1391
|
+
const first = this.__waiting_callback;
|
|
1392
|
+
const more = this.__more_waiting_callbacks;
|
|
1393
|
+
this.__waiting_callback = undefined;
|
|
1394
|
+
this.__more_waiting_callbacks = undefined;
|
|
1395
|
+
first?.call(this, err, dataValue);
|
|
1396
|
+
if (more) {
|
|
1397
|
+
for (const callback1 of more) {
|
|
1398
|
+
callback1.call(this, err, dataValue);
|
|
1399
|
+
}
|
|
1379
1400
|
}
|
|
1380
1401
|
};
|
|
1381
1402
|
const satisfy_callbacks = this.__satisfy_waiting_callbacks;
|
|
@@ -1386,13 +1407,16 @@ export class UAVariableImpl<T extends UAVariableEvents & ListenerSignature<T> =
|
|
|
1386
1407
|
// on a Bad result too (FEAT-34)
|
|
1387
1408
|
satisfy_callbacks(null, makeNowDataValue({ statusCode: denied }));
|
|
1388
1409
|
} else if (typeof this.refreshFunc === "function") {
|
|
1389
|
-
const clock = getCurrentClock();
|
|
1410
|
+
const clock = readAt ?? getCurrentClock();
|
|
1411
|
+
this.__sharedReadClock = readAt !== undefined;
|
|
1390
1412
|
if (this.asyncRefresh === wrappedAsyncRefresh) {
|
|
1391
1413
|
// the thenified wrapper adds nothing when a callback is given
|
|
1392
1414
|
asyncRefreshImpl.call(this, clock, satisfy_callbacks);
|
|
1393
1415
|
} else {
|
|
1394
1416
|
this.asyncRefresh(clock, satisfy_callbacks);
|
|
1395
1417
|
}
|
|
1418
|
+
// consumed by asyncRefresh on entry; cleared here for an overridden one that never reads it
|
|
1419
|
+
this.__sharedReadClock = false;
|
|
1396
1420
|
} else {
|
|
1397
1421
|
assert(this.$dataValue instanceof DataValue);
|
|
1398
1422
|
satisfy_callbacks(null, this.readValue(context));
|
|
@@ -2327,6 +2351,13 @@ function coerceDataValue(dataValue: DataValue | DataValueLike): DataValue {
|
|
|
2327
2351
|
return new DataValue(dataValue);
|
|
2328
2352
|
}
|
|
2329
2353
|
|
|
2354
|
+
/** `clock` is not later than the time given as a Date and picoseconds (compared as they are, with no clock object made) */
|
|
2355
|
+
function clockNotAfter(clock: PreciseClock, timestamp: Date, picoseconds: number): boolean {
|
|
2356
|
+
const a = clock.timestamp.getTime();
|
|
2357
|
+
const b = timestamp.getTime();
|
|
2358
|
+
return a < b || (a === b && clock.picoseconds <= (picoseconds || 0));
|
|
2359
|
+
}
|
|
2360
|
+
|
|
2330
2361
|
// variation #3 :
|
|
2331
2362
|
function _Variable_bind_with_async_refresh(
|
|
2332
2363
|
this: UAVariableImpl,
|
|
@@ -2611,10 +2642,11 @@ export class UAVariableImplT<T, DT extends DataType> extends UAVariableImpl impl
|
|
|
2611
2642
|
}
|
|
2612
2643
|
|
|
2613
2644
|
public readValueAsync(context: ISessionContext | null): Promise<DataValueT<T, DT>>;
|
|
2614
|
-
public readValueAsync(context: ISessionContext | null, callback: CallbackT<DataValueT<T, DT
|
|
2645
|
+
public readValueAsync(context: ISessionContext | null, callback: CallbackT<DataValueT<T, DT>>, clock?: PreciseClock): void;
|
|
2615
2646
|
public readValueAsync(
|
|
2616
2647
|
context: ISessionContext | null,
|
|
2617
|
-
callback?: CallbackT<DataValueT<T, DT
|
|
2648
|
+
callback?: CallbackT<DataValueT<T, DT>>,
|
|
2649
|
+
clock?: PreciseClock
|
|
2618
2650
|
): Promise<DataValueT<T, DT>> | undefined {
|
|
2619
2651
|
// forward the arguments we were actually given: the base method picks the promise or the
|
|
2620
2652
|
// callback form from the callback it receives (writeValue below, still thenified, picks
|
|
@@ -2622,7 +2654,7 @@ export class UAVariableImplT<T, DT extends DataType> extends UAVariableImpl impl
|
|
|
2622
2654
|
if (callback === undefined) {
|
|
2623
2655
|
return super.readValueAsync(context) as Promise<DataValueT<T, DT>>;
|
|
2624
2656
|
}
|
|
2625
|
-
super.readValueAsync(context, callback as CallbackT<DataValue
|
|
2657
|
+
super.readValueAsync(context, callback as CallbackT<DataValue>, clock);
|
|
2626
2658
|
return undefined;
|
|
2627
2659
|
}
|
|
2628
2660
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-opcua-address-space",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "2.187.
|
|
4
|
+
"version": "2.187.11",
|
|
5
5
|
"description": "pure nodejs OPCUA SDK - module address-space",
|
|
6
6
|
"main": "./dist/api/index.js",
|
|
7
7
|
"types": "./dist/api/index.d.ts",
|
|
@@ -23,34 +23,34 @@
|
|
|
23
23
|
"@types/semver": "7.8.0",
|
|
24
24
|
"chalk": "6.0.1",
|
|
25
25
|
"dequeue": "^1.0.5",
|
|
26
|
-
"node-opcua-address-space-base": "2.187.
|
|
27
|
-
"node-opcua-address-space-store": "2.187.
|
|
26
|
+
"node-opcua-address-space-base": "2.187.9",
|
|
27
|
+
"node-opcua-address-space-store": "2.187.9",
|
|
28
28
|
"node-opcua-assert": "2.186.7",
|
|
29
29
|
"node-opcua-basic-types": "2.187.0",
|
|
30
30
|
"node-opcua-binary-stream": "2.187.0",
|
|
31
|
-
"node-opcua-client-dynamic-extension-object": "2.187.
|
|
31
|
+
"node-opcua-client-dynamic-extension-object": "2.187.9",
|
|
32
32
|
"node-opcua-constants": "2.185.0",
|
|
33
33
|
"node-opcua-crypto": "6.2.3",
|
|
34
|
-
"node-opcua-data-access": "2.187.
|
|
34
|
+
"node-opcua-data-access": "2.187.9",
|
|
35
35
|
"node-opcua-data-model": "2.187.0",
|
|
36
|
-
"node-opcua-data-value": "2.187.
|
|
36
|
+
"node-opcua-data-value": "2.187.9",
|
|
37
37
|
"node-opcua-date-time": "2.187.0",
|
|
38
38
|
"node-opcua-debug": "2.186.7",
|
|
39
39
|
"node-opcua-enum": "2.187.0",
|
|
40
40
|
"node-opcua-extension-object": "2.187.0",
|
|
41
41
|
"node-opcua-factory": "2.187.0",
|
|
42
42
|
"node-opcua-nodeid": "2.187.0",
|
|
43
|
-
"node-opcua-nodeset-ua": "2.187.
|
|
43
|
+
"node-opcua-nodeset-ua": "2.187.9",
|
|
44
44
|
"node-opcua-numeric-range": "2.187.0",
|
|
45
45
|
"node-opcua-object-registry": "2.186.7",
|
|
46
|
-
"node-opcua-pseudo-session": "2.187.
|
|
47
|
-
"node-opcua-service-browse": "2.187.
|
|
48
|
-
"node-opcua-service-call": "2.187.
|
|
49
|
-
"node-opcua-service-history": "2.187.
|
|
50
|
-
"node-opcua-service-translate-browse-path": "2.187.
|
|
51
|
-
"node-opcua-service-write": "2.187.
|
|
46
|
+
"node-opcua-pseudo-session": "2.187.9",
|
|
47
|
+
"node-opcua-service-browse": "2.187.9",
|
|
48
|
+
"node-opcua-service-call": "2.187.9",
|
|
49
|
+
"node-opcua-service-history": "2.187.9",
|
|
50
|
+
"node-opcua-service-translate-browse-path": "2.187.9",
|
|
51
|
+
"node-opcua-service-write": "2.187.9",
|
|
52
52
|
"node-opcua-status-code": "2.187.0",
|
|
53
|
-
"node-opcua-types": "2.187.
|
|
53
|
+
"node-opcua-types": "2.187.9",
|
|
54
54
|
"node-opcua-utils": "2.187.0",
|
|
55
55
|
"node-opcua-variant": "2.187.0",
|
|
56
56
|
"node-opcua-xml2json": "2.187.0",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"node-opcua-nodesets": "2.187.4",
|
|
66
66
|
"node-opcua-packet-analyzer": "2.187.0",
|
|
67
67
|
"node-opcua-schemas": "2.187.0",
|
|
68
|
-
"node-opcua-service-filter": "2.187.
|
|
68
|
+
"node-opcua-service-filter": "2.187.9",
|
|
69
69
|
"node-opcua-test-fixtures": "2.184.0",
|
|
70
70
|
"node-opcua-test-helpers": "2.184.0"
|
|
71
71
|
},
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
"internet of things"
|
|
85
85
|
],
|
|
86
86
|
"homepage": "http://node-opcua.github.io/",
|
|
87
|
-
"gitHead": "
|
|
87
|
+
"gitHead": "da8e4d922c74b877e62f5abbd7530354b0e92ee2",
|
|
88
88
|
"files": [
|
|
89
89
|
"dist",
|
|
90
90
|
"distHelpers",
|