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.
Files changed (39) hide show
  1. package/dist/private/client_base_impl.d.ts +11 -4
  2. package/dist/private/client_base_impl.js +130 -70
  3. package/dist/private/client_base_impl.js.map +1 -1
  4. package/dist/private/client_publish_engine.d.ts +9 -4
  5. package/dist/private/client_publish_engine.js +4 -98
  6. package/dist/private/client_publish_engine.js.map +1 -1
  7. package/dist/private/client_session_impl.d.ts +20 -4
  8. package/dist/private/client_session_impl.js +2 -21
  9. package/dist/private/client_session_impl.js.map +1 -1
  10. package/dist/private/client_subscription_impl.d.ts +5 -8
  11. package/dist/private/client_subscription_impl.js +83 -174
  12. package/dist/private/client_subscription_impl.js.map +1 -1
  13. package/dist/private/i_private_client.d.ts +0 -1
  14. package/dist/private/opcua_client_impl.js +1 -1
  15. package/dist/private/opcua_client_impl.js.map +1 -1
  16. package/dist/private/reconnection/client_publish_engine_reconnection.d.ts +2 -0
  17. package/dist/private/reconnection/client_publish_engine_reconnection.js +118 -0
  18. package/dist/private/reconnection/client_publish_engine_reconnection.js.map +1 -0
  19. package/dist/private/reconnection/client_reconnection.d.ts +2 -0
  20. package/dist/private/reconnection/client_reconnection.js +20 -0
  21. package/dist/private/reconnection/client_reconnection.js.map +1 -0
  22. package/dist/private/reconnection/client_subscription_reconnection.d.ts +7 -0
  23. package/dist/private/reconnection/client_subscription_reconnection.js +124 -0
  24. package/dist/private/reconnection/client_subscription_reconnection.js.map +1 -0
  25. package/dist/private/reconnection/reconnection.d.ts +12 -0
  26. package/dist/private/reconnection/reconnection.js +564 -0
  27. package/dist/private/reconnection/reconnection.js.map +1 -0
  28. package/dist/reconnection.js.map +1 -1
  29. package/package.json +24 -24
  30. package/source/private/client_base_impl.ts +142 -83
  31. package/source/private/client_publish_engine.ts +13 -129
  32. package/source/private/client_session_impl.ts +23 -30
  33. package/source/private/client_subscription_impl.ts +85 -207
  34. package/source/private/i_private_client.ts +0 -2
  35. package/source/private/opcua_client_impl.ts +1 -1
  36. package/source/private/reconnection/client_publish_engine_reconnection.ts +146 -0
  37. package/source/private/reconnection/client_reconnection.ts +17 -0
  38. package/source/private/reconnection/client_subscription_reconnection.ts +147 -0
  39. 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, RepublishRequest, RepublishResponse } from "node-opcua-service-subscription";
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
- private readonly subscriptionMap: { [key: number]: ClientSubscriptionImpl };
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(0x7FFFFFFF, this.nbPendingPublishRequests * this.timeoutHint);
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 "../reconnection";
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
  }