node-opcua-client 2.122.0 → 2.124.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/private/client_base_impl.d.ts +11 -4
- package/dist/private/client_base_impl.js +130 -70
- package/dist/private/client_base_impl.js.map +1 -1
- package/dist/private/client_publish_engine.d.ts +9 -4
- package/dist/private/client_publish_engine.js +4 -98
- package/dist/private/client_publish_engine.js.map +1 -1
- package/dist/private/client_session_impl.d.ts +20 -4
- package/dist/private/client_session_impl.js +2 -21
- package/dist/private/client_session_impl.js.map +1 -1
- package/dist/private/client_subscription_impl.d.ts +5 -8
- package/dist/private/client_subscription_impl.js +83 -174
- package/dist/private/client_subscription_impl.js.map +1 -1
- package/dist/private/i_private_client.d.ts +0 -1
- package/dist/private/opcua_client_impl.js +1 -1
- package/dist/private/opcua_client_impl.js.map +1 -1
- package/dist/private/reconnection/client_publish_engine_reconnection.d.ts +2 -0
- package/dist/private/reconnection/client_publish_engine_reconnection.js +118 -0
- package/dist/private/reconnection/client_publish_engine_reconnection.js.map +1 -0
- package/dist/private/reconnection/client_reconnection.d.ts +2 -0
- package/dist/private/reconnection/client_reconnection.js +20 -0
- package/dist/private/reconnection/client_reconnection.js.map +1 -0
- package/dist/private/reconnection/client_subscription_reconnection.d.ts +7 -0
- package/dist/private/reconnection/client_subscription_reconnection.js +124 -0
- package/dist/private/reconnection/client_subscription_reconnection.js.map +1 -0
- package/dist/private/reconnection/reconnection.d.ts +12 -0
- package/dist/private/reconnection/reconnection.js +564 -0
- package/dist/private/reconnection/reconnection.js.map +1 -0
- package/dist/reconnection.js.map +1 -1
- package/package.json +24 -24
- package/source/private/client_base_impl.ts +142 -83
- package/source/private/client_publish_engine.ts +13 -129
- package/source/private/client_session_impl.ts +23 -30
- package/source/private/client_subscription_impl.ts +85 -207
- package/source/private/i_private_client.ts +0 -2
- package/source/private/opcua_client_impl.ts +1 -1
- package/source/private/reconnection/client_publish_engine_reconnection.ts +146 -0
- package/source/private/reconnection/client_reconnection.ts +17 -0
- package/source/private/reconnection/client_subscription_reconnection.ts +147 -0
- package/source/{reconnection.ts → private/reconnection/reconnection.ts} +120 -72
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module node-opcua-client-private
|
|
3
3
|
*/
|
|
4
|
-
import { types } from "util";
|
|
5
|
-
import async from "async";
|
|
6
4
|
import chalk from "chalk";
|
|
5
|
+
import { minDate } from "node-opcua-date-time";
|
|
7
6
|
import { assert } from "node-opcua-assert";
|
|
8
7
|
import { checkDebugFlag, make_debugLog, make_warningLog } from "node-opcua-debug";
|
|
9
|
-
import { PublishRequest, PublishResponse
|
|
10
|
-
import { StatusCodes } from "node-opcua-status-code";
|
|
8
|
+
import { PublishRequest, PublishResponse } from "node-opcua-service-subscription";
|
|
11
9
|
|
|
12
10
|
import { ClientSession, SubscriptionId } from "../client_session";
|
|
13
11
|
import { ClientSubscription } from "../client_subscription";
|
|
@@ -41,7 +39,14 @@ export class ClientSidePublishEngine {
|
|
|
41
39
|
public isSuspended: boolean;
|
|
42
40
|
public session: ClientSession | null;
|
|
43
41
|
private subscriptionAcknowledgements: any[];
|
|
44
|
-
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @internal
|
|
45
|
+
* @private
|
|
46
|
+
*/
|
|
47
|
+
readonly subscriptionMap: { [key: number]: ClientSubscriptionImpl };
|
|
48
|
+
|
|
49
|
+
public lastRequestSentTime: Date = minDate;
|
|
45
50
|
|
|
46
51
|
constructor(session: ClientSession) {
|
|
47
52
|
this.session = session;
|
|
@@ -200,31 +205,6 @@ export class ClientSidePublishEngine {
|
|
|
200
205
|
return Object.prototype.hasOwnProperty.call(this.subscriptionMap, subscriptionId);
|
|
201
206
|
}
|
|
202
207
|
|
|
203
|
-
public republish(callback: () => void): void {
|
|
204
|
-
// After re-establishing the connection the Client shall call Republish in a loop, starting with
|
|
205
|
-
// the next expected sequence number and incrementing the sequence number until the Server returns
|
|
206
|
-
// the status BadMessageNotAvailable.
|
|
207
|
-
// After receiving this status, the Client shall start sending Publish requests with the normal Publish
|
|
208
|
-
// handling.
|
|
209
|
-
// This sequence ensures that the lost NotificationMessages queued in the Server are not overwritten
|
|
210
|
-
// by newPublish responses
|
|
211
|
-
/**
|
|
212
|
-
* call Republish continuously until all Notification messages of
|
|
213
|
-
* un-acknowledged notifications are reprocessed.
|
|
214
|
-
* @private
|
|
215
|
-
*/
|
|
216
|
-
const repairSubscription = (
|
|
217
|
-
subscription: ClientSubscription,
|
|
218
|
-
subscriptionId: SubscriptionId | string,
|
|
219
|
-
innerCallback: () => void
|
|
220
|
-
) => {
|
|
221
|
-
subscriptionId = parseInt(subscriptionId as string, 10);
|
|
222
|
-
this.__repairSubscription(subscription, subscriptionId, innerCallback);
|
|
223
|
-
};
|
|
224
|
-
|
|
225
|
-
async.forEachOf(this.subscriptionMap, repairSubscription, callback);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
208
|
public internalSendPublishRequest(): void {
|
|
229
209
|
assert(this.session, "ClientSidePublishEngine terminated ?");
|
|
230
210
|
|
|
@@ -265,7 +245,7 @@ export class ClientSidePublishEngine {
|
|
|
265
245
|
// in our case:
|
|
266
246
|
|
|
267
247
|
assert(this.nbPendingPublishRequests > 0);
|
|
268
|
-
const calculatedTimeout = Math.min(
|
|
248
|
+
const calculatedTimeout = Math.min(0x7fffffff, this.nbPendingPublishRequests * this.timeoutHint);
|
|
269
249
|
|
|
270
250
|
const publishRequest = new PublishRequest({
|
|
271
251
|
requestHeader: { timeoutHint: calculatedTimeout }, // see note
|
|
@@ -278,6 +258,8 @@ export class ClientSidePublishEngine {
|
|
|
278
258
|
session.publish(publishRequest, (err: Error | null, response?: PublishResponse) => {
|
|
279
259
|
this.nbPendingPublishRequests -= 1;
|
|
280
260
|
|
|
261
|
+
this.lastRequestSentTime = new Date();
|
|
262
|
+
|
|
281
263
|
if (err) {
|
|
282
264
|
debugLog(
|
|
283
265
|
chalk.cyan("ClientSidePublishEngine.prototype.internalSendPublishRequest callback : "),
|
|
@@ -403,102 +385,4 @@ export class ClientSidePublishEngine {
|
|
|
403
385
|
debugLog(" or because there is no session for the subscription (session terminated ?).");
|
|
404
386
|
}
|
|
405
387
|
}
|
|
406
|
-
|
|
407
|
-
private _republish(subscription: any, subscriptionId: SubscriptionId, callback: (err?: Error) => void) {
|
|
408
|
-
assert(subscription.subscriptionId === +subscriptionId);
|
|
409
|
-
|
|
410
|
-
let isDone = false;
|
|
411
|
-
const session = this.session as ClientSessionImpl;
|
|
412
|
-
|
|
413
|
-
const sendRepublishFunc = (callback2: (err?: Error) => void) => {
|
|
414
|
-
assert(isFinite(subscription.lastSequenceNumber) && subscription.lastSequenceNumber + 1 >= 0);
|
|
415
|
-
|
|
416
|
-
const request = new RepublishRequest({
|
|
417
|
-
retransmitSequenceNumber: subscription.lastSequenceNumber + 1,
|
|
418
|
-
subscriptionId: subscription.subscriptionId
|
|
419
|
-
});
|
|
420
|
-
|
|
421
|
-
// istanbul ignore next
|
|
422
|
-
if (doDebug) {
|
|
423
|
-
// istanbul ignore next
|
|
424
|
-
debugLog(
|
|
425
|
-
chalk.bgCyan.yellow.bold(" republish Request for subscription"),
|
|
426
|
-
request.subscriptionId,
|
|
427
|
-
" retransmitSequenceNumber=",
|
|
428
|
-
request.retransmitSequenceNumber
|
|
429
|
-
);
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
if (!session || session!._closeEventHasBeenEmitted) {
|
|
433
|
-
debugLog("ClientPublishEngine#_republish aborted ");
|
|
434
|
-
// has client been disconnected in the mean time ?
|
|
435
|
-
isDone = true;
|
|
436
|
-
return callback2();
|
|
437
|
-
}
|
|
438
|
-
session.republish(request, (err: Error | null, response?: RepublishResponse) => {
|
|
439
|
-
const statusCode = err ? StatusCodes.Bad : response!.responseHeader.serviceResult;
|
|
440
|
-
if (!err && (statusCode.equals(StatusCodes.Good) || statusCode.equals(StatusCodes.BadMessageNotAvailable))) {
|
|
441
|
-
// reprocess notification message and keep going
|
|
442
|
-
if (statusCode.equals(StatusCodes.Good)) {
|
|
443
|
-
subscription.onNotificationMessage(response!.notificationMessage);
|
|
444
|
-
}
|
|
445
|
-
} else {
|
|
446
|
-
if (!err) {
|
|
447
|
-
err = new Error(response!.responseHeader.serviceResult.toString());
|
|
448
|
-
}
|
|
449
|
-
debugLog(" _send_republish ends with ", err.message);
|
|
450
|
-
isDone = true;
|
|
451
|
-
}
|
|
452
|
-
callback2(err ? err : undefined);
|
|
453
|
-
});
|
|
454
|
-
};
|
|
455
|
-
|
|
456
|
-
setImmediate(() => {
|
|
457
|
-
assert(typeof callback === "function");
|
|
458
|
-
async.whilst(
|
|
459
|
-
(cb: (err: null, truth: boolean) => void) => cb(null, !isDone),
|
|
460
|
-
sendRepublishFunc,
|
|
461
|
-
((err?: Error | null): void => {
|
|
462
|
-
debugLog("nbPendingPublishRequest = ", this.nbPendingPublishRequests);
|
|
463
|
-
debugLog(" _republish ends with ", err ? err.message : "null");
|
|
464
|
-
callback(err!);
|
|
465
|
-
}) as any // Wait for @type/async bug to be fixed !
|
|
466
|
-
);
|
|
467
|
-
});
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
private __repairSubscription(
|
|
471
|
-
subscription: ClientSubscription,
|
|
472
|
-
subscriptionId: SubscriptionId,
|
|
473
|
-
callback: (err?: Error) => void
|
|
474
|
-
) {
|
|
475
|
-
debugLog("__repairSubscription for SubscriptionId ", subscriptionId);
|
|
476
|
-
|
|
477
|
-
this._republish(subscription, subscriptionId, (err?: Error) => {
|
|
478
|
-
assert(!err || types.isNativeError(err));
|
|
479
|
-
|
|
480
|
-
debugLog("__repairSubscription--------------------- err =", err ? err.message : null);
|
|
481
|
-
|
|
482
|
-
if (err && err.message.match(/BadSessionInvalid/)) {
|
|
483
|
-
// _republish failed because session is not valid anymore on server side.
|
|
484
|
-
return callback(err);
|
|
485
|
-
}
|
|
486
|
-
if (err && err.message.match(/SubscriptionIdInvalid/)) {
|
|
487
|
-
// _republish failed because subscriptionId is not valid anymore on server side.
|
|
488
|
-
//
|
|
489
|
-
// This could happen when the subscription has timed out and has been deleted by server
|
|
490
|
-
// Subscription may time out if the duration of the connection break exceed the max life time
|
|
491
|
-
// of the subscription.
|
|
492
|
-
//
|
|
493
|
-
// In this case, Client must recreate a subscription and recreate monitored item without altering
|
|
494
|
-
// the event handlers
|
|
495
|
-
//
|
|
496
|
-
debugLog(chalk.bgWhite.red("_republish failed " + " subscriptionId is not valid anymore on server side."));
|
|
497
|
-
|
|
498
|
-
const subscriptionI = subscription as ClientSubscriptionImpl;
|
|
499
|
-
return subscriptionI.recreateSubscriptionAndMonitoredItem(callback);
|
|
500
|
-
}
|
|
501
|
-
callback();
|
|
502
|
-
});
|
|
503
|
-
}
|
|
504
388
|
}
|
|
@@ -124,11 +124,11 @@ import {
|
|
|
124
124
|
HistoryReadValueIdOptions2,
|
|
125
125
|
ExtraReadHistoryValueParameters
|
|
126
126
|
} from "../client_session";
|
|
127
|
+
import { UserIdentityInfo } from "../user_identity_info";
|
|
127
128
|
import { ClientSessionKeepAliveManager } from "../client_session_keepalive_manager";
|
|
128
129
|
import { ClientSubscription } from "../client_subscription";
|
|
129
130
|
import { Request, Response } from "../common";
|
|
130
|
-
import { repair_client_session } from "
|
|
131
|
-
import { UserIdentityInfo } from "../user_identity_info";
|
|
131
|
+
import { repair_client_session } from "./reconnection/reconnection";
|
|
132
132
|
|
|
133
133
|
import { ClientSidePublishEngine } from "./client_publish_engine";
|
|
134
134
|
import { ClientSubscriptionImpl } from "./client_subscription_impl";
|
|
@@ -1012,7 +1012,25 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession {
|
|
|
1012
1012
|
* @param value {Variant} - the value to write
|
|
1013
1013
|
* @return {Promise<StatusCode>} - the status code of the write
|
|
1014
1014
|
*
|
|
1015
|
-
* @deprecated
|
|
1015
|
+
* @deprecated use session.write instead
|
|
1016
|
+
*
|
|
1017
|
+
* @example
|
|
1018
|
+
* // please use session.write instead of session.writeSingleNode
|
|
1019
|
+
* // as follow
|
|
1020
|
+
* const statusCode = await session.write({
|
|
1021
|
+
* nodeId,
|
|
1022
|
+
* attributeId: AttributeIds.Value,
|
|
1023
|
+
* value: {
|
|
1024
|
+
* statusCode: Good,
|
|
1025
|
+
* sourceTimestamp: new Date(), // optional, some server may not accept this
|
|
1026
|
+
* value: {
|
|
1027
|
+
* dataType: opcua.DataType.Double,
|
|
1028
|
+
* value: 100.0
|
|
1029
|
+
* }
|
|
1030
|
+
* }
|
|
1031
|
+
* });
|
|
1032
|
+
*
|
|
1033
|
+
*
|
|
1016
1034
|
*/
|
|
1017
1035
|
public writeSingleNode(nodeId: NodeIdLike, value: VariantLike, callback: ResponseCallback<StatusCode>): void;
|
|
1018
1036
|
|
|
@@ -1474,28 +1492,12 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession {
|
|
|
1474
1492
|
return this._client !== null && this._client._secureChannel !== null && this._client._secureChannel.isOpened();
|
|
1475
1493
|
}
|
|
1476
1494
|
|
|
1477
|
-
private requestReconnection() {
|
|
1478
|
-
if (this._client) {
|
|
1479
|
-
this._client.requestReconnection();
|
|
1480
|
-
assert(this._client.isReconnecting === true, "expecting client to be reconnecting now");
|
|
1481
|
-
}
|
|
1482
|
-
}
|
|
1483
|
-
|
|
1484
1495
|
public performMessageTransaction(request: Request, callback: (err: Error | null, response?: Response) => void): void {
|
|
1485
1496
|
if (!this._client) {
|
|
1486
1497
|
// session may have been closed by user ... but is still in used !!
|
|
1487
1498
|
return callback(new Error("Session has been closed and should not be used to perform a transaction anymore"));
|
|
1488
1499
|
}
|
|
1489
1500
|
|
|
1490
|
-
// if (!this.isChannelValid()) {
|
|
1491
|
-
// // the secure channel is broken, may be the server has crashed or the network cable has been disconnected
|
|
1492
|
-
// // for a long time
|
|
1493
|
-
// // we may need to queue this transaction, as a secure token may be being reprocessed
|
|
1494
|
-
// errorLog(chalk.bgWhite.red("!!! Performing transaction on invalid channel !!! ", request.schema.name));
|
|
1495
|
-
// // this.requestReconnection();
|
|
1496
|
-
// return callback(new Error("!!! Performing transaction on invalid channel with " + request.schema.name + ": starting reconnection process"));
|
|
1497
|
-
// }
|
|
1498
|
-
|
|
1499
1501
|
this._reconnecting.pendingTransactions = this._reconnecting.pendingTransactions || [];
|
|
1500
1502
|
this._reconnecting.pendingTransactionsCount = this._reconnecting.pendingTransactionsCount || 0;
|
|
1501
1503
|
|
|
@@ -2014,13 +2016,6 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession {
|
|
|
2014
2016
|
.catch(callback);
|
|
2015
2017
|
}
|
|
2016
2018
|
|
|
2017
|
-
public resumePublishEngine(): void {
|
|
2018
|
-
assert(this._publishEngine);
|
|
2019
|
-
if (this._publishEngine && this._publishEngine.subscriptionCount > 0) {
|
|
2020
|
-
this._publishEngine.replenish_publish_request_queue();
|
|
2021
|
-
}
|
|
2022
|
-
}
|
|
2023
|
-
|
|
2024
2019
|
public async readNamespaceArray(): Promise<string[]>;
|
|
2025
2020
|
public readNamespaceArray(callback: (err: Error | null, namespaceArray?: string[]) => void): void;
|
|
2026
2021
|
public readNamespaceArray(...args: any[]): any {
|
|
@@ -2205,6 +2200,7 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession {
|
|
|
2205
2200
|
request: Request,
|
|
2206
2201
|
callback: (err: Error | null, response?: Response) => void
|
|
2207
2202
|
) {
|
|
2203
|
+
warningLog("attempt to recreate session to reperform a transation ", request.constructor.name);
|
|
2208
2204
|
if (this.recursive_repair_detector >= 1) {
|
|
2209
2205
|
// tslint:disable-next-line: no-console
|
|
2210
2206
|
warningLog("recreate_session_and_reperform_transaction => Already in Progress");
|
|
@@ -2230,10 +2226,7 @@ type promoteOpaqueStructure3WithCallbackFunc = (
|
|
|
2230
2226
|
callback: ErrorCallback
|
|
2231
2227
|
) => void;
|
|
2232
2228
|
|
|
2233
|
-
async function promoteOpaqueStructure2(
|
|
2234
|
-
session: IBasicSessionAsync2,
|
|
2235
|
-
callMethodResult: CallMethodResult
|
|
2236
|
-
): Promise<void> {
|
|
2229
|
+
async function promoteOpaqueStructure2(session: IBasicSessionAsync2, callMethodResult: CallMethodResult): Promise<void> {
|
|
2237
2230
|
if (!callMethodResult || !callMethodResult.outputArguments || callMethodResult.outputArguments.length === 0) {
|
|
2238
2231
|
return;
|
|
2239
2232
|
}
|