node-opcua-client 2.123.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 (49) hide show
  1. package/dist/alarms_and_conditions/client_alarm_tools.js.map +1 -1
  2. package/dist/alarms_and_conditions/client_tools.js.map +1 -1
  3. package/dist/client_monitored_item_toolbox.js.map +1 -1
  4. package/dist/client_session_keepalive_manager.js.map +1 -1
  5. package/dist/client_utils.js.map +1 -1
  6. package/dist/private/client_base_impl.d.ts +11 -4
  7. package/dist/private/client_base_impl.js +130 -70
  8. package/dist/private/client_base_impl.js.map +1 -1
  9. package/dist/private/client_monitored_item_group_impl.js.map +1 -1
  10. package/dist/private/client_monitored_item_impl.js.map +1 -1
  11. package/dist/private/client_publish_engine.d.ts +9 -4
  12. package/dist/private/client_publish_engine.js +5 -99
  13. package/dist/private/client_publish_engine.js.map +1 -1
  14. package/dist/private/client_session_impl.d.ts +20 -4
  15. package/dist/private/client_session_impl.js +5 -24
  16. package/dist/private/client_session_impl.js.map +1 -1
  17. package/dist/private/client_subscription_impl.d.ts +5 -8
  18. package/dist/private/client_subscription_impl.js +83 -174
  19. package/dist/private/client_subscription_impl.js.map +1 -1
  20. package/dist/private/i_private_client.d.ts +0 -1
  21. package/dist/private/opcua_client_impl.js +1 -1
  22. package/dist/private/opcua_client_impl.js.map +1 -1
  23. package/dist/private/performance.js.map +1 -1
  24. package/dist/private/reconnection/client_publish_engine_reconnection.d.ts +2 -0
  25. package/dist/private/reconnection/client_publish_engine_reconnection.js +118 -0
  26. package/dist/private/reconnection/client_publish_engine_reconnection.js.map +1 -0
  27. package/dist/private/reconnection/client_reconnection.d.ts +2 -0
  28. package/dist/private/reconnection/client_reconnection.js +20 -0
  29. package/dist/private/reconnection/client_reconnection.js.map +1 -0
  30. package/dist/private/reconnection/client_subscription_reconnection.d.ts +7 -0
  31. package/dist/private/reconnection/client_subscription_reconnection.js +124 -0
  32. package/dist/private/reconnection/client_subscription_reconnection.js.map +1 -0
  33. package/dist/private/reconnection/reconnection.d.ts +12 -0
  34. package/dist/private/reconnection/reconnection.js +564 -0
  35. package/dist/private/reconnection/reconnection.js.map +1 -0
  36. package/dist/tools/findservers.js.map +1 -1
  37. package/dist/tools/read_history_server_capabilities.js.map +1 -1
  38. package/dist/verify.js.map +1 -1
  39. package/package.json +8 -8
  40. package/source/private/client_base_impl.ts +136 -81
  41. package/source/private/client_publish_engine.ts +13 -129
  42. package/source/private/client_session_impl.ts +23 -30
  43. package/source/private/client_subscription_impl.ts +85 -207
  44. package/source/private/i_private_client.ts +0 -2
  45. package/source/private/opcua_client_impl.ts +1 -1
  46. package/source/private/reconnection/client_publish_engine_reconnection.ts +146 -0
  47. package/source/private/reconnection/client_reconnection.ts +17 -0
  48. package/source/private/reconnection/client_subscription_reconnection.ts +147 -0
  49. package/source/{reconnection.ts → private/reconnection/reconnection.ts} +120 -72
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-opcua-client",
3
- "version": "2.123.0",
3
+ "version": "2.124.0",
4
4
  "description": "pure nodejs OPCUA SDK - module client",
5
5
  "scripts": {
6
6
  "clean": "npx rimraf node_modules dist *.tsbuildinfo certificates",
@@ -17,12 +17,12 @@
17
17
  "@types/async": "^3.2.24",
18
18
  "async": "^3.2.5",
19
19
  "chalk": "4.1.2",
20
- "node-opcua-alarm-condition": "2.123.0",
20
+ "node-opcua-alarm-condition": "2.124.0",
21
21
  "node-opcua-assert": "2.120.0",
22
22
  "node-opcua-basic-types": "2.121.0",
23
23
  "node-opcua-buffer-utils": "2.120.0",
24
24
  "node-opcua-certificate-manager": "2.121.0",
25
- "node-opcua-client-dynamic-extension-object": "2.123.0",
25
+ "node-opcua-client-dynamic-extension-object": "2.124.0",
26
26
  "node-opcua-common": "2.123.0",
27
27
  "node-opcua-constants": "2.121.0",
28
28
  "node-opcua-crypto": "4.8.0",
@@ -35,9 +35,9 @@
35
35
  "node-opcua-nodeid": "2.121.0",
36
36
  "node-opcua-object-registry": "2.121.0",
37
37
  "node-opcua-pki": "4.10.0",
38
- "node-opcua-pseudo-session": "2.123.0",
38
+ "node-opcua-pseudo-session": "2.124.0",
39
39
  "node-opcua-schemas": "2.123.0",
40
- "node-opcua-secure-channel": "2.123.0",
40
+ "node-opcua-secure-channel": "2.124.0",
41
41
  "node-opcua-service-browse": "2.123.0",
42
42
  "node-opcua-service-call": "2.123.0",
43
43
  "node-opcua-service-discovery": "2.123.0",
@@ -49,7 +49,7 @@
49
49
  "node-opcua-service-register-node": "2.123.0",
50
50
  "node-opcua-service-secure-channel": "2.123.0",
51
51
  "node-opcua-service-session": "2.123.0",
52
- "node-opcua-service-subscription": "2.123.0",
52
+ "node-opcua-service-subscription": "2.124.0",
53
53
  "node-opcua-service-translate-browse-path": "2.123.0",
54
54
  "node-opcua-service-write": "2.123.0",
55
55
  "node-opcua-status-code": "2.121.0",
@@ -62,7 +62,7 @@
62
62
  "@types/underscore": "^1.11.15",
63
63
  "dequeue": "^1.0.5",
64
64
  "mocha": "^10.3.0",
65
- "node-opcua-address-space": "2.123.0",
65
+ "node-opcua-address-space": "2.124.0",
66
66
  "node-opcua-binary-stream": "2.120.0",
67
67
  "node-opcua-factory": "2.121.0",
68
68
  "node-opcua-leak-detector": "2.121.0",
@@ -91,7 +91,7 @@
91
91
  "internet of things"
92
92
  ],
93
93
  "homepage": "http://node-opcua.github.io/",
94
- "gitHead": "64b4575687f6a8130e06fa78b6f61b6eeaa05e74",
94
+ "gitHead": "75b5e34e797094a83833ea2d7c72ca3e5983d648",
95
95
  "files": [
96
96
  "dist",
97
97
  "source"
@@ -66,6 +66,7 @@ import { UserIdentityInfo } from "../user_identity_info";
66
66
  import { performCertificateSanityCheck } from "../verify";
67
67
  import { ClientSessionImpl } from "./client_session_impl";
68
68
  import { IClientBase } from "./i_private_client";
69
+ import { waitUntilReconnectionIsCanceled } from "./reconnection/client_reconnection";
69
70
 
70
71
  const debugLog = make_debugLog(__filename);
71
72
  const doDebug = checkDebugFlag(__filename);
@@ -80,13 +81,13 @@ const defaultConnectionStrategy: ConnectionStrategyOptions = {
80
81
  randomisationFactor: 0.1
81
82
  };
82
83
 
83
- interface MasterClient extends OPCUAClientBase {
84
- _tmpClient?: OPCUAClientBase;
85
- }
86
- function __findEndpoint(this: OPCUAClientBase, endpointUrl: string, params: FindEndpointOptions, _callback: FindEndpointCallback) {
87
- const masterClient = this as MasterClient;
88
- debugLog("findEndpoint : endpointUrl = ", endpointUrl);
89
- debugLog(" params ", params);
84
+ function __findEndpoint(this: ClientBaseImpl, endpointUrl: string, params: FindEndpointOptions, _callback: FindEndpointCallback) {
85
+ if (this.isUnusable()) {
86
+ return _callback(new Error("Client is not usable"));
87
+ }
88
+ const masterClient = this as ClientBaseImpl;
89
+ doDebug && debugLog("findEndpoint : endpointUrl = ", endpointUrl);
90
+ doDebug && debugLog(" params ", params);
90
91
  assert(!masterClient._tmpClient);
91
92
 
92
93
  const callback = (err: Error | null, result?: FindEndpointResult) => {
@@ -104,11 +105,13 @@ function __findEndpoint(this: OPCUAClientBase, endpointUrl: string, params: Find
104
105
  clientCertificateManager: params.clientCertificateManager,
105
106
 
106
107
  clientName: "EndpointFetcher",
107
- connectionStrategy: {
108
- maxRetry: 0 /* no- retry */,
109
- maxDelay: 2000
110
- },
111
108
 
109
+ // use same connectionStrategy as parent
110
+ connectionStrategy: params.connectionStrategy,
111
+ // connectionStrategy: {
112
+ // maxRetry: 0 /* no- retry */,
113
+ // maxDelay: 2000
114
+ // },
112
115
  privateKeyFile: params.privateKeyFile
113
116
  };
114
117
 
@@ -323,7 +326,11 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
323
326
  * true if the client is trying to reconnect to the server after a connection break.
324
327
  */
325
328
  get isReconnecting(): boolean {
326
- return !!(this._secureChannel && this._secureChannel.isConnecting) || this._internalState !== "connected";
329
+ return (
330
+ !!(this._secureChannel && this._secureChannel.isConnecting) ||
331
+ this._internalState === "reconnecting_newchannel_connected" ||
332
+ this._internalState === "reconnecting"
333
+ );
327
334
  }
328
335
 
329
336
  /**
@@ -370,7 +377,6 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
370
377
  public _sessions: ClientSessionImpl[];
371
378
  protected _serverEndpoints: EndpointDescription[];
372
379
  public _secureChannel: ClientSecureChannelLayer | null;
373
- protected disconnecting: boolean;
374
380
 
375
381
  // statistics...
376
382
  private _byteRead: number;
@@ -381,7 +387,7 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
381
387
  private _transactionsPerformed: number;
382
388
  private _reconnectionIsCanceled: boolean;
383
389
  private _clockAdjuster?: ClockAdjustment;
384
- private _tmpClient?: OPCUAClientBase;
390
+ protected _tmpClient?: OPCUAClientBase;
385
391
  private _instanceNumber: number;
386
392
  private _transportSettings: TransportSettings;
387
393
  private _transportTimeout?: number;
@@ -389,7 +395,12 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
389
395
  public clientCertificateManager: OPCUACertificateManager;
390
396
 
391
397
  public isUnusable() {
392
- return this._internalState === "disconnected" || this._internalState === "disconnecting";
398
+ return (
399
+ this._internalState === "disconnected" ||
400
+ this._internalState === "disconnecting" ||
401
+ this._internalState === "panic" ||
402
+ this._internalState === "uninitialized"
403
+ );
393
404
  }
394
405
 
395
406
  protected _setInternalState(internalState: InternalClientState): void {
@@ -402,6 +413,12 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
402
413
  chalk.yellow(internalState)
403
414
  );
404
415
  }
416
+ if (this._internalState === "disconnecting" || this._internalState === "disconnected") {
417
+ if (internalState === "reconnecting") {
418
+ errorLog("Internal error, cannot switch to reconnecting when already disconnecting");
419
+ } // when disconnecting, we cannot accept any other state
420
+ }
421
+
405
422
  this._internalState = internalState;
406
423
  }
407
424
  public emit(eventName: string | symbol, ...others: any[]): boolean {
@@ -422,8 +439,10 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
422
439
 
423
440
  super(options as IOPCUASecureObjectOptions);
424
441
 
442
+ this._setInternalState("uninitialized");
443
+
425
444
  this._instanceNumber = g_ClientCounter++;
426
- this._internalState = "uninitialized";
445
+
427
446
  this.applicationName = options.applicationName || "NodeOPCUA-Client";
428
447
  assert(!this.applicationName.match(/^locale=/), "applicationName badly converted from LocalizedText");
429
448
  assert(!this.applicationName.match(/urn:/), "applicationName should not be a URI");
@@ -431,8 +450,6 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
431
450
  // we need to delay _applicationUri initialization
432
451
  this._applicationUri = options.applicationUri || this._getBuiltApplicationUri();
433
452
 
434
- this.disconnecting = false;
435
-
436
453
  this.clientCertificateManager = options.clientCertificateManager;
437
454
 
438
455
  this._secureChannel = null;
@@ -491,13 +508,13 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
491
508
  warningLog("internal error: _cancel_reconnection should only be used when reconnecting is in progress");
492
509
  }
493
510
 
494
- debugLog("canceling reconnection");
511
+ debugLog("canceling reconnection : ", this.clientName);
495
512
 
496
513
  this._reconnectionIsCanceled = true;
497
514
 
498
515
  // istanbul ignore next
499
516
  if (!this._secureChannel) {
500
- debugLog("_cancel_reconnection: Nothing to do !");
517
+ debugLog("_cancel_reconnection: Nothing to do for !", this.clientName, " because secure channel doesn't exist");
501
518
  return callback(); // nothing to do
502
519
  }
503
520
 
@@ -566,6 +583,12 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
566
583
  if (this._reconnectionIsCanceled) {
567
584
  return _when_reconnectionIsCanceled(callback);
568
585
  }
586
+ if (this._secureChannel) {
587
+ doDebug && debugLog("attempt to recreate a new secure channel, while channel already exists");
588
+ // are we reentrant ?
589
+ const err = new Error("_internal_create_secure_channel failed, this._secureChannel is supposed to be null");
590
+ return _when_internal_error(err, callback);
591
+ }
569
592
  assert(!this._secureChannel, "_attempt_to_recreate_secure_channel, expecting this._secureChannel not to exist");
570
593
 
571
594
  this._internal_create_secure_channel(infiniteConnectionRetry, (err?: Error | null) => {
@@ -649,7 +672,11 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
649
672
  assert(this._secureChannel === null);
650
673
  assert(typeof this.endpointUrl === "string");
651
674
 
652
- debugLog("_internal_create_secure_channel creating new ClientSecureChannelLayer _internalState =", this._internalState);
675
+ debugLog(
676
+ "_internal_create_secure_channel creating new ClientSecureChannelLayer _internalState =",
677
+ this._internalState,
678
+ this.clientName
679
+ );
653
680
  const secureChannel = new ClientSecureChannelLayer({
654
681
  connectionStrategy,
655
682
  defaultSecureTokenLifetime: this.defaultSecureTokenLifetime,
@@ -683,7 +710,6 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
683
710
  debugLog("_internal_create_secure_channel after secureChannel.create");
684
711
  if (!this._secureChannel) {
685
712
  debugLog("_secureChannel has been closed during the transaction !");
686
- assert(this.disconnecting);
687
713
  return innerCallback(new Error("Secure Channel Closed"));
688
714
  }
689
715
  if (!err) {
@@ -696,10 +722,11 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
696
722
  (innerCallback: ErrorCallback) => {
697
723
  assert(this._secureChannel !== null);
698
724
  if (!this.knowsServerEndpoint) {
725
+ this._setInternalState("connecting");
726
+
699
727
  this.getEndpoints((err: Error | null /*, endpoints?: EndpointDescription[]*/) => {
700
728
  if (!this._secureChannel) {
701
729
  debugLog("_secureChannel has been closed during the transaction ! (while getEndpoints)");
702
- assert(this.disconnecting);
703
730
  return innerCallback(new Error("Secure Channel Closed"));
704
731
  }
705
732
  innerCallback(err ? err : undefined);
@@ -712,7 +739,7 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
712
739
  ],
713
740
  (err) => {
714
741
  if (err) {
715
- doDebug && debugLog("Inner create secure channel has failed", err.message);
742
+ doDebug && debugLog(this.clientName, " : Inner create secure channel has failed", err.message);
716
743
  if (this._secureChannel) {
717
744
  this._secureChannel!.abortConnection(() => {
718
745
  this._destroy_secure_channel();
@@ -767,7 +794,6 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
767
794
  return;
768
795
  }
769
796
  warningLog("Creating default certificate ... please wait");
770
- if (this.disconnecting) return;
771
797
 
772
798
  await ClientBaseImpl.createCertificate(
773
799
  this.clientCertificateManager,
@@ -809,14 +835,14 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
809
835
  if (!fs.existsSync(this.privateKeyFile)) {
810
836
  throw new Error(" cannot locate private key file " + this.privateKeyFile);
811
837
  }
812
- if (this.disconnecting) return;
838
+ if (this.isUnusable()) return;
813
839
 
814
840
  await this.clientCertificateManager.withLock2(async () => {
815
841
  await performCertificateSanityCheck(this, "client", this.clientCertificateManager, this._getBuiltApplicationUri());
816
842
  });
817
843
  }
818
844
 
819
- protected _internalState: InternalClientState;
845
+ protected _internalState: InternalClientState = "uninitialized";
820
846
 
821
847
  protected _handleUnrecoverableConnectionFailure(err: Error, callback: ErrorCallback): void {
822
848
  debugLog(err.message);
@@ -867,12 +893,11 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
867
893
  }
868
894
 
869
895
  this._setInternalState("connecting");
870
- this.disconnecting = false;
871
896
 
872
897
  this.initializeCM()
873
898
  .then(() => {
874
- debugLog("ClientBaseImpl#connect ", endpointUrl);
875
- if (this.disconnecting) {
899
+ debugLog("ClientBaseImpl#connect ", endpointUrl, this.clientName);
900
+ if (this._internalState === "disconnecting" || this._internalState === "disconnected") {
876
901
  return this._handleDisconnectionWhileConnecting(new Error("premature disconnection 1"), callback);
877
902
  }
878
903
 
@@ -885,7 +910,7 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
885
910
  if (err) {
886
911
  return this._handleUnrecoverableConnectionFailure(err, callback);
887
912
  }
888
- if (this.disconnecting) {
913
+ if (this.isUnusable()) {
889
914
  return this._handleDisconnectionWhileConnecting(new Error("premature disconnection 2"), callback);
890
915
  }
891
916
  if (forceEndpointDiscoveryOnConnect) {
@@ -916,7 +941,8 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
916
941
  this._clockAdjuster = this._clockAdjuster || new ClockAdjustment();
917
942
  OPCUAClientBase.registry.register(this);
918
943
 
919
- debugLog("__connectStep2");
944
+ debugLog("__connectStep2 ", this._internalState);
945
+
920
946
  this._internal_create_secure_channel(this.connectionStrategy, (err: Error | null) => {
921
947
  if (!err) {
922
948
  this._handleSuccessfulConnection(callback);
@@ -958,16 +984,6 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
958
984
  return this._secureChannel ? this._secureChannel.getClientNonce() : null;
959
985
  }
960
986
 
961
- public requestReconnection() {
962
- if (this._secureChannel) {
963
- this._secureChannel.abortConnection(() => {
964
- errorLog("new connection requested");
965
- /*
966
- */
967
- });
968
- }
969
- }
970
-
971
987
  public performMessageTransaction(request: Request, callback: ResponseCallback<Response>): void {
972
988
  if (!this._secureChannel) {
973
989
  // this may happen if the Server has closed the connection abruptly for some unknown reason
@@ -984,7 +1000,7 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
984
1000
  ) {
985
1001
  return callback(
986
1002
  new Error(
987
- "performMessageTransaction: Invalid client state " +
1003
+ "performMessageTransaction: Invalid client state = " +
988
1004
  this._internalState +
989
1005
  " while performing a transaction " +
990
1006
  request.schema.name
@@ -1173,11 +1189,6 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
1173
1189
 
1174
1190
  debugLog(chalk.bgWhite.green("_closeSession ") + this._secureChannel!.channelId);
1175
1191
 
1176
- if (this.isReconnecting) {
1177
- errorLog("OPCUAClientImpl#_closeSession called while reconnection in progress ! What shall we do");
1178
- return callback(null);
1179
- }
1180
-
1181
1192
  const request = new CloseSessionRequest({
1182
1193
  deleteSubscriptions
1183
1194
  });
@@ -1222,27 +1233,27 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
1222
1233
  public disconnect(...args: any[]): any {
1223
1234
  const callback = args[0];
1224
1235
  assert(typeof callback === "function", "expecting a callback function here");
1225
-
1226
- if (this._internalState === "disconnected" || this._internalState === "disconnecting") {
1227
- if (this._internalState === "disconnecting") {
1228
- warningLog("[NODE-OPCUA-W26] OPCUAClient#disconnect called while already disconnecting");
1229
- }
1230
- return callback();
1231
- }
1232
- debugLog("disconnecting client! (will set reconnectionIsCanceled to true");
1233
1236
  this._reconnectionIsCanceled = true;
1234
- this.disconnecting = true;
1235
-
1236
1237
  if (this._tmpClient) {
1238
+ warningLog("disconnecting client while tmpClient exists", this._tmpClient.clientName);
1237
1239
  this._tmpClient.disconnect((err) => {
1238
1240
  this._tmpClient = undefined;
1239
- assert(!this._tmpClient);
1240
1241
  // retry disconnect on main client
1241
1242
  this.disconnect(callback);
1242
1243
  });
1243
1244
  return;
1244
1245
  }
1246
+ if (this._internalState === "disconnected" || this._internalState === "disconnecting") {
1247
+ if (this._internalState === "disconnecting") {
1248
+ warningLog("[NODE-OPCUA-W26] OPCUAClient#disconnect called while already disconnecting");
1249
+ }
1250
+ return callback();
1251
+ }
1252
+ debugLog("disconnecting client! (will set reconnectionIsCanceled to true");
1253
+ this._reconnectionIsCanceled = true;
1254
+
1245
1255
  debugLog("ClientBaseImpl#disconnect", this.endpointUrl);
1256
+
1246
1257
  if (this.isReconnecting && !this._reconnectionIsCanceled) {
1247
1258
  debugLog("ClientBaseImpl#disconnect called while reconnection is in progress");
1248
1259
  // let's abort the reconnection process
@@ -1386,6 +1397,9 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
1386
1397
  const params = {
1387
1398
  connectionStrategy: this.connectionStrategy,
1388
1399
  endpointMustExist: false,
1400
+
1401
+ // Node: May be the discovery endpoint only support security mode NONE
1402
+ // what should we do ?
1389
1403
  securityMode: this.securityMode,
1390
1404
  securityPolicy: this.securityPolicy,
1391
1405
 
@@ -1593,21 +1607,25 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
1593
1607
  debugLog(chalk.yellow.bold(" ClientBaseImpl emitting close"), err?.message);
1594
1608
  this._destroy_secure_channel();
1595
1609
  if (!err || !this.reconnectOnFailure) {
1596
- // this is a normal close operation initiated by us
1597
- /**
1598
- * @event close
1599
- * @param error
1600
- */
1601
1610
  if (err) {
1611
+ /**
1612
+ * @event connection_lost
1613
+ */
1602
1614
  this.emit("connection_lost", err?.message); // instead of "close"
1603
1615
  }
1616
+ // this is a normal close operation initiated by us
1604
1617
  this.emit("close", err); // instead of "close"
1605
1618
  } else {
1606
- /**
1607
- * @event connection_lost
1608
- */
1609
- if (this.reconnectOnFailure && this._internalState !== "reconnecting") {
1619
+ if (
1620
+ this.reconnectOnFailure &&
1621
+ this._internalState !== "reconnecting" &&
1622
+ this._internalState !== "reconnecting_newchannel_connected"
1623
+ ) {
1610
1624
  debugLog(" ClientBaseImpl emitting connection_lost");
1625
+ this._setInternalState("reconnecting");
1626
+ /**
1627
+ * @event connection_lost
1628
+ */
1611
1629
  this.emit("connection_lost", err?.message); // instead of "close"
1612
1630
  this._repairConnection();
1613
1631
  }
@@ -1624,14 +1642,44 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
1624
1642
  });
1625
1643
  }
1626
1644
 
1627
- public _inside_repairConnection = false;
1645
+ #insideRepairConnection = false;
1646
+
1647
+ #shouldRepairAgain = false;
1628
1648
 
1649
+ /**
1650
+ * @internal
1651
+ * @private
1652
+ *
1653
+ * timeout to wait before client attempt to reconnect in case of failure
1654
+ *
1655
+ */
1656
+ static retryDelay = 1000;
1629
1657
  private _repairConnection() {
1630
- if (this._inside_repairConnection) {
1631
- errorLog("_repairConnection already in progress ", this._internalState);
1658
+ doDebug && debugLog("_repairConnection = ", this._internalState);
1659
+ if (this.isUnusable()) return;
1660
+
1661
+ const duration = ClientBaseImpl.retryDelay;
1662
+ if (duration) {
1663
+ this.emit("startingDelayBeforeReconnection", duration);
1664
+ setTimeout(() => {
1665
+ if (this.isUnusable()) return;
1666
+ this.__innerRepairConnection();
1667
+ }, duration);
1668
+ } else {
1669
+ this.__innerRepairConnection();
1670
+ }
1671
+ }
1672
+ private __innerRepairConnection() {
1673
+ if (this.isUnusable()) return;
1674
+
1675
+ debugLog("Entering _repairConnection ", this._internalState);
1676
+ if (this.#insideRepairConnection) {
1677
+ errorLog("_repairConnection already in progress internal state = ", this._internalState);
1678
+ this.#shouldRepairAgain = true;
1632
1679
  return;
1633
1680
  }
1634
- this._inside_repairConnection = true;
1681
+ this.emit("repairConnectionStarted");
1682
+ this.#insideRepairConnection = true;
1635
1683
  debugLog("recreating new secure channel ", this._internalState);
1636
1684
  this._recreate_secure_channel((err1?: Error) => {
1637
1685
  debugLog("secureChannel#on(close) => _recreate_secure_channel returns ", err1 ? err1.message : "OK");
@@ -1639,10 +1687,16 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
1639
1687
  if (err1) {
1640
1688
  debugLog("_recreate_secure_channel has failed: err = ", err1.message);
1641
1689
  this.emit("close", err1);
1642
- this._setInternalState("disconnected");
1643
- this._inside_repairConnection = false;
1690
+ this.#insideRepairConnection = false;
1691
+ if (this.#shouldRepairAgain) {
1692
+ this.#shouldRepairAgain = false;
1693
+ this._repairConnection();
1694
+ } else {
1695
+ this._setInternalState("disconnected");
1696
+ }
1644
1697
  return;
1645
1698
  } else {
1699
+ if (this.isUnusable()) return;
1646
1700
  this._finalReconnectionStep((err2?: Error | null) => {
1647
1701
  if (err2) {
1648
1702
  // istanbul ignore next
@@ -1653,16 +1707,21 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
1653
1707
  // we still need to retry connecting here !!!
1654
1708
  debugLog("Disconnected following reconnection failure", err2.message);
1655
1709
  debugLog(`I will retry OPCUA client reconnection in ${OPCUAClientBase.retryDelay / 1000} seconds`);
1656
- this._inside_repairConnection = false;
1710
+ this.#insideRepairConnection = false;
1711
+ this.#shouldRepairAgain = false;
1712
+
1657
1713
  this._destroy_secure_channel();
1714
+ // this._setInternalState("reconnecting_failed");
1658
1715
  setTimeout(() => this._repairConnection(), OPCUAClientBase.retryDelay);
1716
+
1659
1717
  return;
1660
1718
  } else {
1661
1719
  /**
1662
1720
  * @event connection_reestablished
1663
1721
  * send when the connection is reestablished after a connection break
1664
1722
  */
1665
- this._inside_repairConnection = false;
1723
+ this.#insideRepairConnection = false;
1724
+ this.#shouldRepairAgain = false;
1666
1725
  this._setInternalState("connected");
1667
1726
  this.emit("connection_reestablished");
1668
1727
  }
@@ -1720,13 +1779,9 @@ class TmpClient extends ClientBaseImpl {
1720
1779
  return;
1721
1780
  }
1722
1781
 
1723
- if (this.disconnecting) {
1724
- return this._handleUnrecoverableConnectionFailure(new Error("premature disconnection 3"), callback!);
1725
- }
1726
-
1727
1782
  this._setInternalState("connecting");
1728
1783
  this._connectStep2(endpoint, (err?: Error) => {
1729
- if (this.disconnecting) {
1784
+ if (this.isUnusable()) {
1730
1785
  return this._handleUnrecoverableConnectionFailure(new Error("premature disconnection 4"), callback!);
1731
1786
  }
1732
1787
  callback!(err);