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
@@ -3,7 +3,6 @@
3
3
  */
4
4
  // tslint:disable:unified-signatures
5
5
  import { EventEmitter } from "events";
6
- import async from "async";
7
6
  import chalk from "chalk";
8
7
 
9
8
  import { assert } from "node-opcua-assert";
@@ -13,14 +12,11 @@ import { resolveNodeId } from "node-opcua-nodeid";
13
12
 
14
13
  import { ReadValueIdOptions, TimestampsToReturn } from "node-opcua-service-read";
15
14
  import {
16
- CreateMonitoredItemsRequest,
17
- CreateMonitoredItemsResponse,
18
15
  CreateSubscriptionRequest,
19
16
  CreateSubscriptionResponse,
20
17
  DataChangeNotification,
21
18
  DeleteMonitoredItemsResponse,
22
19
  DeleteSubscriptionsResponse,
23
- MonitoredItemCreateRequestOptions,
24
20
  MonitoringParametersOptions,
25
21
  NotificationMessage,
26
22
  StatusChangeNotification,
@@ -37,9 +33,7 @@ import { StatusCode, StatusCodes } from "node-opcua-status-code";
37
33
  import { Callback, ErrorCallback } from "node-opcua-status-code";
38
34
  import { isNullOrUndefined } from "node-opcua-utils";
39
35
  import { promoteOpaqueStructureInNotificationData } from "node-opcua-client-dynamic-extension-object";
40
- import { createMonitoredItemsLimit, IBasicSessionReadAsync, readOperationLimits } from "node-opcua-pseudo-session";
41
36
 
42
- import { IBasicSessionWithSubscription } from "node-opcua-pseudo-session";
43
37
  import { ClientMonitoredItemBase } from "../client_monitored_item_base";
44
38
  import { ClientMonitoredItemGroup } from "../client_monitored_item_group";
45
39
  import { ClientSession, MonitoredItemData, SubscriptionId } from "../client_session";
@@ -59,13 +53,13 @@ import { ClientSidePublishEngine } from "./client_publish_engine";
59
53
  import { ClientSessionImpl } from "./client_session_impl";
60
54
  import { detectLongOperation } from "./performance";
61
55
 
62
- const debugLog = make_debugLog(__filename);
63
- const doDebug = checkDebugFlag(__filename);
64
- const warningLog = make_warningLog(__filename);
56
+ const debugLog = make_debugLog("CLIENT_SUBSCRIPTION");
57
+ const doDebug = checkDebugFlag("CLIENT_SUBSCRIPTION");
58
+ const warningLog = make_warningLog("CLIENT_SUBSCRIPTION");
65
59
 
66
- const PENDING_SUBSCRIPTION_ID = 0xc0cac01a;
67
- const TERMINATED_SUBSCRIPTION_ID = 0xc0cac01b;
68
- const TERMINATING_SUBSCRIPTION_ID = 0xc0cac01c;
60
+ export const PENDING_SUBSCRIPTION_ID = 0xc0cac01a;
61
+ export const TERMINATED_SUBSCRIPTION_ID = 0xc0cac01b;
62
+ export const TERMINATING_SUBSCRIPTION_ID = 0xc0cac01c;
69
63
 
70
64
  const minimumMaxKeepAliveCount = 3;
71
65
 
@@ -122,20 +116,6 @@ function displayKeepAliveWarning(sessionTimeout: number, maxKeepAliveCount: numb
122
116
  return false;
123
117
  }
124
118
 
125
- function createMonitoredItemsAndRespectOperationalLimits(
126
- session: IBasicSessionReadAsync & IBasicSessionWithSubscription,
127
- createMonitorItemsRequest: CreateMonitoredItemsRequest,
128
- callback: (err: Error | null, response?: CreateMonitoredItemsResponse) => void
129
- ) {
130
- readOperationLimits(session)
131
- .then((operationalLimits) => {
132
- createMonitoredItemsLimit(operationalLimits.maxMonitoredItemsPerCall || 0, session, createMonitorItemsRequest)
133
- .then((createMonitoredItemResponse) => callback(null, createMonitoredItemResponse))
134
- .catch(callback);
135
- })
136
- .catch(callback);
137
- }
138
-
139
119
  export class ClientSubscriptionImpl extends EventEmitter implements ClientSubscription {
140
120
  /**
141
121
  * the associated session
@@ -171,9 +151,8 @@ export class ClientSubscriptionImpl extends EventEmitter implements ClientSubscr
171
151
  public publishEngine: ClientSidePublishEngine;
172
152
 
173
153
  public lastSequenceNumber: number;
174
- private lastRequestSentTime: Date;
175
154
  private _nextClientHandle = 0;
176
- private hasTimedOut: boolean;
155
+ public hasTimedOut: boolean;
177
156
 
178
157
  constructor(session: ClientSession, options: ClientSubscriptionOptions) {
179
158
  super();
@@ -219,7 +198,7 @@ export class ClientSubscriptionImpl extends EventEmitter implements ClientSubscr
219
198
  this.lifetimeCount = options.requestedLifetimeCount;
220
199
  this.maxKeepAliveCount = options.requestedMaxKeepAliveCount;
221
200
  this.maxNotificationsPerPublish = options.maxNotificationsPerPublish || 0;
222
- this.publishingEnabled = options.publishingEnabled;
201
+ this.publishingEnabled = options.publishingEnabled === undefined ? true : options.publishingEnabled;
223
202
  this.priority = options.priority;
224
203
 
225
204
  this.subscriptionId = PENDING_SUBSCRIPTION_ID;
@@ -227,8 +206,6 @@ export class ClientSubscriptionImpl extends EventEmitter implements ClientSubscr
227
206
  this._nextClientHandle = 0;
228
207
  this.monitoredItems = {};
229
208
 
230
- this.lastRequestSentTime = new Date(1, 1, 1970);
231
-
232
209
  /**
233
210
  * set to True when the server has notified us that this subscription has timed out
234
211
  * ( maxLifeCounter x published interval without being able to process a PublishRequest
@@ -238,7 +215,7 @@ export class ClientSubscriptionImpl extends EventEmitter implements ClientSubscr
238
215
  this.hasTimedOut = false;
239
216
 
240
217
  setImmediate(() => {
241
- this.__create_subscription((err?: Error) => {
218
+ __create_subscription(this, (err?: Error) => {
242
219
  if (!err) {
243
220
  setImmediate(() => {
244
221
  /**
@@ -536,103 +513,6 @@ export class ClientSubscriptionImpl extends EventEmitter implements ClientSubscr
536
513
  this.session.getMonitoredItems(this.subscriptionId, args[0]);
537
514
  }
538
515
 
539
- /**
540
- * utility function to recreate new subscription
541
- * @method recreateSubscriptionAndMonitoredItem
542
- */
543
- public recreateSubscriptionAndMonitoredItem(callback: ErrorCallback): void {
544
- debugLog("ClientSubscription#recreateSubscriptionAndMonitoredItem");
545
-
546
- if (this.subscriptionId === TERMINATED_SUBSCRIPTION_ID) {
547
- debugLog("Subscription is not in a valid state");
548
- return callback();
549
- }
550
-
551
- const oldMonitoredItems = this.monitoredItems;
552
-
553
- this.publishEngine.unregisterSubscription(this.subscriptionId);
554
-
555
- async.series(
556
- [
557
- (innerCallback: ErrorCallback) => {
558
- this.__create_subscription(innerCallback);
559
- },
560
- (innerCallback: ErrorCallback) => {
561
- const test = this.publishEngine.getSubscription(this.subscriptionId);
562
-
563
- debugLog("recreating ", Object.keys(oldMonitoredItems).length, " monitored Items");
564
- // re-create monitored items
565
- const itemsToCreate: MonitoredItemCreateRequestOptions[] = [];
566
-
567
- for (const monitoredItem of Object.values(oldMonitoredItems)) {
568
- assert(monitoredItem.monitoringParameters.clientHandle > 0);
569
- itemsToCreate.push({
570
- itemToMonitor: monitoredItem.itemToMonitor,
571
- monitoringMode: monitoredItem.monitoringMode,
572
- requestedParameters: monitoredItem.monitoringParameters
573
- });
574
- }
575
-
576
- const createMonitorItemsRequest = new CreateMonitoredItemsRequest({
577
- itemsToCreate,
578
- subscriptionId: this.subscriptionId,
579
- timestampsToReturn: TimestampsToReturn.Both // this.timestampsToReturn,
580
- });
581
-
582
- const session = this.session;
583
- // istanbul ignore next
584
- if (!session) {
585
- return innerCallback(new Error("no session"));
586
- }
587
-
588
- debugLog("Recreating ", itemsToCreate.length, " monitored items");
589
-
590
- createMonitoredItemsAndRespectOperationalLimits(
591
- session,
592
- createMonitorItemsRequest,
593
- (err: Error | null, response?: CreateMonitoredItemsResponse) => {
594
- if (err) {
595
- debugLog("Recreating monitored item has failed with ", err.message);
596
- return innerCallback(err);
597
- }
598
- /* istanbul ignore next */
599
- if (!response) {
600
- return innerCallback(new Error("Internal Error"));
601
- }
602
- const monitoredItemResults = response.results || [];
603
-
604
- monitoredItemResults.forEach((monitoredItemResult, index) => {
605
- const itemToCreate = itemsToCreate[index];
606
- /* istanbul ignore next */
607
- if (!itemToCreate || !itemToCreate.requestedParameters) {
608
- throw new Error("Internal Error");
609
- }
610
- const clientHandle = itemToCreate.requestedParameters.clientHandle;
611
- /* istanbul ignore next */
612
- if (!clientHandle) {
613
- throw new Error("Internal Error");
614
- }
615
- const monitoredItem = this.monitoredItems[clientHandle] as ClientMonitoredItemImpl;
616
- if (monitoredItem) {
617
- monitoredItem._applyResult(monitoredItemResult);
618
- } else {
619
- warningLog("cannot find monitored item for clientHandle !:", clientHandle);
620
- }
621
- });
622
- innerCallback();
623
- }
624
- );
625
- }
626
- ],
627
- (err) => {
628
- if (err) {
629
- warningLog(err.message);
630
- }
631
- callback(err!);
632
- }
633
- );
634
- }
635
-
636
516
  public toString(): string {
637
517
  let str = "";
638
518
  str += "subscriptionId : " + this.subscriptionId + "\n";
@@ -642,9 +522,12 @@ export class ClientSubscriptionImpl extends EventEmitter implements ClientSubscr
642
522
  str += "hasTimedOut : " + this.hasTimedOut + "\n";
643
523
 
644
524
  const timeToLive = this.lifetimeCount * this.publishingInterval;
645
- str += "timeToLive : " + timeToLive + "\n";
646
- str += "lastRequestSentTime : " + this.lastRequestSentTime.toString() + "\n";
647
- const duration = Date.now() - this.lastRequestSentTime.getTime();
525
+ str += "(maxKeepAliveCount*publishingInterval: " + this.publishingInterval * this.maxKeepAliveCount + " ms)\n";
526
+ str += "(maxLifetimeCount*publishingInterval: " + timeToLive + " ms)\n";
527
+
528
+ const lastRequestSentTime = this.publishEngine.lastRequestSentTime;
529
+ str += "lastRequestSentTime : " + lastRequestSentTime.toString() + "\n";
530
+ const duration = Date.now() - lastRequestSentTime.getTime();
648
531
  const extra =
649
532
  duration - timeToLive > 0
650
533
  ? chalk.red(" expired since " + (duration - timeToLive) / 1000 + " seconds")
@@ -654,7 +537,6 @@ export class ClientSubscriptionImpl extends EventEmitter implements ClientSubscr
654
537
  str += "has expired : " + (duration > timeToLive) + "\n";
655
538
 
656
539
  str += "(session timeout : " + this.session.timeout + " ms)\n";
657
- str += "(maxKeepAliveCount*publishingInterval: " + this.publishingInterval * this.session.timeout + " ms)\n";
658
540
 
659
541
  return str;
660
542
  }
@@ -665,7 +547,8 @@ export class ClientSubscriptionImpl extends EventEmitter implements ClientSubscr
665
547
  public evaluateRemainingLifetime(): number {
666
548
  const now = Date.now();
667
549
  const timeout = this.publishingInterval * this.lifetimeCount;
668
- const expiryTime = this.lastRequestSentTime.getTime() + timeout;
550
+ const lastRequestSentTime = this.publishEngine.lastRequestSentTime;
551
+ const expiryTime = lastRequestSentTime.getTime() + timeout;
669
552
  return Math.max(0, expiryTime - now);
670
553
  }
671
554
 
@@ -710,75 +593,6 @@ export class ClientSubscriptionImpl extends EventEmitter implements ClientSubscr
710
593
  setImmediate(waitForSubscriptionAndMonitor);
711
594
  }
712
595
 
713
- private __create_subscription(callback: ErrorCallback) {
714
- assert(typeof callback === "function");
715
-
716
- // istanbul ignore next
717
- if (!this.hasSession) {
718
- return callback(new Error("No Session"));
719
- }
720
- const session = this.session;
721
-
722
- debugLog(chalk.yellow.bold("ClientSubscription created "));
723
-
724
- const request = new CreateSubscriptionRequest({
725
- maxNotificationsPerPublish: this.maxNotificationsPerPublish,
726
- priority: this.priority,
727
- publishingEnabled: this.publishingEnabled,
728
- requestedLifetimeCount: this.lifetimeCount,
729
- requestedMaxKeepAliveCount: this.maxKeepAliveCount,
730
- requestedPublishingInterval: this.publishingInterval
731
- });
732
-
733
- session.createSubscription(request, (err: Error | null, response?: CreateSubscriptionResponse) => {
734
- if (err) {
735
- /* istanbul ignore next */
736
- this.emit("internal_error", err);
737
- if (callback) {
738
- return callback(err);
739
- }
740
- return;
741
- }
742
-
743
- /* istanbul ignore next */
744
- if (!response) {
745
- return callback(new Error("internal error"));
746
- }
747
-
748
- if (!this.hasSession) {
749
- return callback(new Error("createSubscription has failed = > no session"));
750
- }
751
- assert(this.hasSession);
752
-
753
- this.subscriptionId = response.subscriptionId;
754
- this.publishingInterval = response.revisedPublishingInterval;
755
- this.lifetimeCount = response.revisedLifetimeCount;
756
- this.maxKeepAliveCount = response.revisedMaxKeepAliveCount;
757
-
758
- this.timeoutHint = Math.min((this.maxKeepAliveCount + 10) * this.publishingInterval * 2, 0x7ffff);
759
-
760
- displayKeepAliveWarning(this.session.timeout, this.maxKeepAliveCount, this.publishingInterval);
761
- ClientSubscription.ignoreNextWarning = false;
762
-
763
- // istanbul ignore next
764
- if (doDebug) {
765
- debugLog(chalk.yellow.bold("registering callback"));
766
- debugLog(chalk.yellow.bold("publishingInterval "), this.publishingInterval);
767
- debugLog(chalk.yellow.bold("lifetimeCount "), this.lifetimeCount);
768
- debugLog(chalk.yellow.bold("maxKeepAliveCount "), this.maxKeepAliveCount);
769
- debugLog(chalk.yellow.bold("publish request timeout hint = "), this.timeoutHint);
770
- debugLog(chalk.yellow.bold("hasTimedOut "), this.hasTimedOut);
771
- debugLog(chalk.yellow.bold("timeoutHint for publish request "), this.timeoutHint);
772
- }
773
-
774
- this.publishEngine.registerSubscription(this);
775
-
776
- if (callback) {
777
- callback();
778
- }
779
- });
780
- }
781
-
782
596
  private __on_publish_response_DataChangeNotification(notification: DataChangeNotification) {
783
597
  assert(notification.schema.name === "DataChangeNotification");
784
598
 
@@ -939,7 +753,7 @@ export class ClientSubscriptionImpl extends EventEmitter implements ClientSubscr
939
753
  warningLog(
940
754
  `[NODE-OPCUA-W32]}: monitored.item event handler takes too much time : operation duration ${duration} ms [repeated ${
941
755
  s(this).$_slowNotifCount
942
- } times]\n please ensure that your monitoreItem event handler is not blocking the event loop.`
756
+ } times]\n please ensure that your monitoredItem event handler is not blocking the event loop.`
943
757
  );
944
758
  }
945
759
  );
@@ -1057,12 +871,76 @@ ClientSubscriptionImpl.prototype.monitor = thenify.withCallback(ClientSubscripti
1057
871
  ClientSubscriptionImpl.prototype.monitorItems = thenify.withCallback(ClientSubscriptionImpl.prototype.monitorItems);
1058
872
  ClientSubscriptionImpl.prototype.setTriggering = thenify.withCallback(ClientSubscriptionImpl.prototype.setTriggering);
1059
873
  ClientSubscriptionImpl.prototype.modify = thenify.withCallback(ClientSubscriptionImpl.prototype.modify);
1060
- ClientSubscriptionImpl.prototype.recreateSubscriptionAndMonitoredItem = thenify.withCallback(
1061
- ClientSubscriptionImpl.prototype.recreateSubscriptionAndMonitoredItem
1062
- );
1063
874
  ClientSubscriptionImpl.prototype.terminate = thenify.withCallback(ClientSubscriptionImpl.prototype.terminate);
1064
875
  ClientSubscriptionImpl.prototype.getMonitoredItems = thenify.withCallback(ClientSubscriptionImpl.prototype.getMonitoredItems);
1065
876
 
1066
877
  ClientSubscription.create = (clientSession: ClientSession, options: ClientSubscriptionOptions) => {
1067
878
  return new ClientSubscriptionImpl(clientSession, options);
1068
879
  };
880
+
881
+ export function __create_subscription(subscription: ClientSubscriptionImpl, callback: ErrorCallback) {
882
+ // istanbul ignore next
883
+ if (!subscription.hasSession) {
884
+ return callback(new Error("__create_subscription: subscription has no Session"));
885
+ }
886
+ const session = subscription.session;
887
+
888
+ debugLog(chalk.yellow.bold("ClientSubscription created "));
889
+
890
+ const request = new CreateSubscriptionRequest({
891
+ maxNotificationsPerPublish: subscription.maxNotificationsPerPublish,
892
+ priority: subscription.priority,
893
+ publishingEnabled: subscription.publishingEnabled,
894
+ requestedLifetimeCount: subscription.lifetimeCount,
895
+ requestedMaxKeepAliveCount: subscription.maxKeepAliveCount,
896
+ requestedPublishingInterval: subscription.publishingInterval
897
+ });
898
+
899
+ session.createSubscription(request, (err: Error | null, response?: CreateSubscriptionResponse) => {
900
+ if (err) {
901
+ /* istanbul ignore next */
902
+ subscription.emit("internal_error", err);
903
+ if (callback) {
904
+ return callback(err);
905
+ }
906
+ return;
907
+ }
908
+
909
+ /* istanbul ignore next */
910
+ if (!response) {
911
+ return callback(new Error("internal error"));
912
+ }
913
+
914
+ if (!subscription.hasSession) {
915
+ return callback(new Error("createSubscription has failed = > no session"));
916
+ }
917
+ assert(subscription.hasSession);
918
+
919
+ subscription.subscriptionId = response.subscriptionId;
920
+ subscription.publishingInterval = response.revisedPublishingInterval;
921
+ subscription.lifetimeCount = response.revisedLifetimeCount;
922
+ subscription.maxKeepAliveCount = response.revisedMaxKeepAliveCount;
923
+
924
+ subscription.timeoutHint = Math.min((subscription.maxKeepAliveCount + 10) * subscription.publishingInterval * 2, 0x7ffff);
925
+
926
+ displayKeepAliveWarning(subscription.session.timeout, subscription.maxKeepAliveCount, subscription.publishingInterval);
927
+ ClientSubscription.ignoreNextWarning = false;
928
+
929
+ // istanbul ignore next
930
+ if (doDebug) {
931
+ debugLog(chalk.yellow.bold("registering callback"));
932
+ debugLog(chalk.yellow.bold("publishingInterval "), subscription.publishingInterval);
933
+ debugLog(chalk.yellow.bold("lifetimeCount "), subscription.lifetimeCount);
934
+ debugLog(chalk.yellow.bold("maxKeepAliveCount "), subscription.maxKeepAliveCount);
935
+ debugLog(chalk.yellow.bold("publish request timeout hint = "), subscription.timeoutHint);
936
+ debugLog(chalk.yellow.bold("hasTimedOut "), subscription.hasTimedOut);
937
+ debugLog(chalk.yellow.bold("timeoutHint for publish request "), subscription.timeoutHint);
938
+ }
939
+
940
+ subscription.publishEngine.registerSubscription(subscription);
941
+
942
+ if (callback) {
943
+ callback();
944
+ }
945
+ });
946
+ }
@@ -27,7 +27,5 @@ export interface IClientBase {
27
27
 
28
28
  getTransportSettings(): IBasicTransportSettings;
29
29
 
30
- requestReconnection(): void;
31
-
32
30
  isUnusable(): boolean;
33
31
  }
@@ -51,7 +51,7 @@ import {
51
51
  } from "../opcua_client";
52
52
  import { AnonymousIdentity, UserIdentityInfo, UserIdentityInfoUserName, UserIdentityInfoX509 } from "../user_identity_info";
53
53
 
54
- import { repair_client_sessions } from "../reconnection";
54
+ import { repair_client_sessions } from "./reconnection/reconnection";
55
55
  import { ClientBaseImpl } from "./client_base_impl";
56
56
  import { ClientSessionImpl } from "./client_session_impl";
57
57
  import { ClientSubscriptionImpl } from "./client_subscription_impl";
@@ -0,0 +1,146 @@
1
+ import { types } from "util";
2
+ import async from "async";
3
+ import chalk from "chalk";
4
+ import assert from "node-opcua-assert";
5
+ import { checkDebugFlag, make_debugLog } from "node-opcua-debug";
6
+ import { StatusCodes } from "node-opcua-status-code";
7
+ import { RepublishRequest, RepublishResponse } from "node-opcua-types";
8
+ import { SubscriptionId } from "../../client_session";
9
+ import { ClientSessionImpl } from "../client_session_impl";
10
+ import { ClientSubscriptionImpl } from "../client_subscription_impl";
11
+ import { ClientSidePublishEngine } from "../client_publish_engine";
12
+ import { recreateSubscriptionAndMonitoredItem } from "./client_subscription_reconnection";
13
+ import { _shouldNotContinue2 } from "./reconnection";
14
+
15
+ const debugLog = make_debugLog("RECONNECTION");
16
+ const doDebug = checkDebugFlag("RECONNECTION");
17
+
18
+ function _republish(
19
+ engine: ClientSidePublishEngine,
20
+ subscription: ClientSubscriptionImpl,
21
+ callback: (err?: Error) => void
22
+ ) {
23
+
24
+ let isDone = false;
25
+ const session = engine.session as ClientSessionImpl;
26
+
27
+ const sendRepublishFunc = (callback2: (err?: Error) => void) => {
28
+ assert(isFinite(subscription.lastSequenceNumber) && subscription.lastSequenceNumber + 1 >= 0);
29
+
30
+ const request = new RepublishRequest({
31
+ retransmitSequenceNumber: subscription.lastSequenceNumber + 1,
32
+ subscriptionId: subscription.subscriptionId
33
+ });
34
+
35
+ // istanbul ignore next
36
+ if (doDebug) {
37
+ // istanbul ignore next
38
+ debugLog(
39
+ chalk.bgCyan.yellow.bold(" republish Request for subscription"),
40
+ request.subscriptionId,
41
+ " retransmitSequenceNumber=",
42
+ request.retransmitSequenceNumber
43
+ );
44
+ }
45
+
46
+ if (!session || session!._closeEventHasBeenEmitted) {
47
+ debugLog("ClientPublishEngine#_republish aborted ");
48
+ // has client been disconnected in the mean time ?
49
+ isDone = true;
50
+ return callback2();
51
+ }
52
+ session.republish(request, (err: Error | null, response?: RepublishResponse) => {
53
+ const statusCode = err ? StatusCodes.Bad : response!.responseHeader.serviceResult;
54
+ if (!err && (statusCode.equals(StatusCodes.Good) || statusCode.equals(StatusCodes.BadMessageNotAvailable))) {
55
+ // reprocess notification message and keep going
56
+ if (statusCode.equals(StatusCodes.Good)) {
57
+ subscription.onNotificationMessage(response!.notificationMessage);
58
+ }
59
+ } else {
60
+ if (!err) {
61
+ err = new Error(response!.responseHeader.serviceResult.toString());
62
+ }
63
+ debugLog(" _send_republish ends with ", err.message);
64
+ isDone = true;
65
+ }
66
+ callback2(err ? err : undefined);
67
+ });
68
+ };
69
+
70
+ const sendRepublishUntilDone = () => {
71
+ async.whilst(
72
+ (cb: (err: null, truth: boolean) => void) => cb(null, !isDone),
73
+ sendRepublishFunc,
74
+ ((err?: Error | null): void => {
75
+ debugLog("nbPendingPublishRequest = ", engine.nbPendingPublishRequests);
76
+ debugLog(" _republish ends with ", err ? err.message : "null");
77
+ callback(err!);
78
+ }) as any // Wait for @type/async bug to be fixed !
79
+ );
80
+ };
81
+
82
+ setImmediate(sendRepublishUntilDone);
83
+ }
84
+
85
+ function __askSubscriptionRepublish(
86
+ engine: ClientSidePublishEngine,
87
+ subscription: ClientSubscriptionImpl,
88
+ callback: (err?: Error) => void
89
+ ) {
90
+
91
+ _republish(engine, subscription, (err?: Error) => {
92
+ // prettier-ignore
93
+ { const _err = _shouldNotContinue2(subscription); if (_err) { return callback(_err); } }
94
+
95
+ assert(!err || types.isNativeError(err));
96
+
97
+ debugLog("__askSubscriptionRepublish--------------------- err =", err ? err.message : null);
98
+
99
+ if (err && err.message.match(/BadSessionInvalid/)) {
100
+ // _republish failed because session is not valid anymore on server side.
101
+ return callback(err);
102
+ }
103
+ if (err && err.message.match(/SubscriptionIdInvalid/)) {
104
+ // _republish failed because subscriptionId is not valid anymore on server side.
105
+ //
106
+ // This could happen when the subscription has timed out and has been deleted by server
107
+ // Subscription may time out if the duration of the connection break exceed the max life time
108
+ // of the subscription.
109
+ //
110
+ // In this case, Client must recreate a subscription and recreate monitored item without altering
111
+ // the event handlers
112
+ //
113
+ debugLog(
114
+ chalk.bgWhite.red("__askSubscriptionRepublish failed " + " subscriptionId is not valid anymore on server side.")
115
+ );
116
+ return recreateSubscriptionAndMonitoredItem(subscription, callback);
117
+ }
118
+ if (err && err.message.match(/|MessageNotAvailable/)) {
119
+ // start engine and start monitoring
120
+ }
121
+ callback();
122
+ });
123
+ }
124
+
125
+ export function republish(engine: ClientSidePublishEngine, callback: () => void): void {
126
+ // After re-establishing the connection the Client shall call Republish in a loop, starting with
127
+ // the next expected sequence number and incrementing the sequence number until the Server returns
128
+ // the status BadMessageNotAvailable.
129
+ // After receiving this status, the Client shall start sending Publish requests with the normal Publish
130
+ // handling.
131
+ // This sequence ensures that the lost NotificationMessages queued in the Server are not overwritten
132
+ // by newPublish responses
133
+ /**
134
+ * call Republish continuously until all Notification messages of
135
+ * un-acknowledged notifications are reprocessed.
136
+ */
137
+ const askSubscriptionRepublish = (
138
+ subscription: ClientSubscriptionImpl,
139
+ subscriptionId: SubscriptionId | string,
140
+ innerCallback: () => void
141
+ ) => {
142
+ __askSubscriptionRepublish(engine, subscription, innerCallback);
143
+ };
144
+
145
+ async.forEachOf(engine.subscriptionMap, askSubscriptionRepublish, callback);
146
+ }
@@ -0,0 +1,17 @@
1
+ import { warn } from "console";
2
+ import { ClientBaseImpl } from "../client_base_impl";
3
+
4
+ export function waitUntilReconnectionIsCanceled(client: ClientBaseImpl, callback: () => void) {
5
+ const interval = 100;
6
+ const maxIntervalCount = 100;
7
+ let intervalCount = 0;
8
+ const timer = setInterval(() => {
9
+ if (!client.isReconnecting || ++intervalCount > maxIntervalCount) {
10
+ clearInterval(timer);
11
+ if (intervalCount > maxIntervalCount) {
12
+ warn("waitUntilReconnectionIsCanceled: timeout");
13
+ }
14
+ callback();
15
+ }
16
+ }, interval);
17
+ }