node-opcua-client 2.123.0 → 2.125.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 (51) 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 +43 -43
  40. package/source/index.ts +4 -1
  41. package/source/opcua_client.ts +11 -5
  42. package/source/private/client_base_impl.ts +136 -81
  43. package/source/private/client_publish_engine.ts +13 -129
  44. package/source/private/client_session_impl.ts +56 -78
  45. package/source/private/client_subscription_impl.ts +85 -207
  46. package/source/private/i_private_client.ts +0 -2
  47. package/source/private/opcua_client_impl.ts +16 -149
  48. package/source/private/reconnection/client_publish_engine_reconnection.ts +146 -0
  49. package/source/private/reconnection/client_reconnection.ts +17 -0
  50. package/source/private/reconnection/client_subscription_reconnection.ts +124 -0
  51. package/source/{reconnection.ts → private/reconnection/reconnection.ts} +121 -76
@@ -3,7 +3,7 @@
3
3
  */
4
4
 
5
5
  // tslint:disable:only-arrow-functions
6
- import * as async from "async";
6
+ import async from "async";
7
7
  import chalk from "chalk";
8
8
  import { assert } from "node-opcua-assert";
9
9
  import { checkDebugFlag, make_debugLog, make_errorLog, make_warningLog } from "node-opcua-debug";
@@ -13,16 +13,37 @@ import { ErrorCallback } from "node-opcua-status-code";
13
13
  import { CloseSessionRequest } from "node-opcua-types";
14
14
  import { invalidateExtraDataTypeManager } from "node-opcua-client-dynamic-extension-object";
15
15
 
16
- import { SubscriptionId } from "./client_session";
17
- import { ClientSessionImpl, Reconnectable } from "./private/client_session_impl";
18
- import { ClientSubscriptionImpl } from "./private/client_subscription_impl";
19
- import { IClientBase } from "./private/i_private_client";
16
+ import { SubscriptionId } from "../../client_session";
17
+ import { ClientSessionImpl, Reconnectable } from "../client_session_impl";
18
+ import { ClientSubscriptionImpl } from "../client_subscription_impl";
19
+ import { IClientBase } from "../i_private_client";
20
+ import { republish } from "./client_publish_engine_reconnection";
21
+ import { recreateSubscriptionAndMonitoredItem } from "./client_subscription_reconnection";
20
22
 
21
23
  const debugLog = make_debugLog("RECONNECTION");
22
24
  const doDebug = checkDebugFlag("RECONNECTION");
23
25
  const errorLog = make_errorLog("RECONNECTION");
24
26
  const warningLog = make_warningLog("RECONNECTION");
25
27
 
28
+ function _shouldNotContinue3(client: IClientBase) {
29
+ if (!client._secureChannel) {
30
+ return new Error("Failure during reconnection : client or session is not usable anymore");
31
+ }
32
+ return null;
33
+ }
34
+
35
+ export function _shouldNotContinue(session: ClientSessionImpl) {
36
+ if (!session._client || session.hasBeenClosed() || !session._client._secureChannel || session._client.isUnusable()) {
37
+ return new Error("Failure during reconnection : client or session is not usable anymore");
38
+ }
39
+ return null;
40
+ }
41
+ export function _shouldNotContinue2(subscription: ClientSubscriptionImpl) {
42
+ if (!subscription.hasSession) {
43
+ return new Error("Failure during reconnection : client or session is not usable anymore");
44
+ }
45
+ return _shouldNotContinue(subscription.session);
46
+ }
26
47
  //
27
48
  // a new secure channel has be created, we need to reactivate the corresponding session,
28
49
  // and reestablish the subscription and restart the publish engine.
@@ -109,29 +130,26 @@ const warningLog = make_warningLog("RECONNECTION");
109
130
  // +-------------------------------+
110
131
 
111
132
  function _ask_for_subscription_republish(session: ClientSessionImpl, callback: (err?: Error) => void) {
112
- if (session.hasBeenClosed()) {
113
- doDebug && debugLog("_ask_for_subscription_republish : session is closed");
114
- return callback(new Error("askForSubscriptionRepublish => canceled because session is closed"));
115
- }
133
+ // prettier-ignore
134
+ { const err = _shouldNotContinue(session); if (err) { return callback(err); } }
116
135
 
117
136
  doDebug && debugLog(chalk.bgCyan.yellow.bold("_ask_for_subscription_republish "));
118
137
  // assert(session.getPublishEngine().nbPendingPublishRequests === 0,
119
138
  // "at this time, publish request queue shall still be empty");
120
139
 
121
- session.getPublishEngine().republish((err?: Error) => {
140
+ const engine = session.getPublishEngine();
141
+ republish(engine, (err?: Error) => {
122
142
  doDebug && debugLog("_ask_for_subscription_republish : republish sent");
123
- if (session.hasBeenClosed()) {
124
- return callback(new Error("Cannot complete subscription republish due to session termination"));
125
- }
143
+ // prettier-ignore
144
+ { const err = _shouldNotContinue(session); if (err) { return callback(err); } }
145
+
126
146
  doDebug && debugLog(chalk.bgCyan.green.bold("_ask_for_subscription_republish done "), err ? err.message : "OK");
127
147
  if (err) {
128
148
  warningLog("republish has failed with error :", err.message);
129
-
130
149
  doDebug && debugLog("_ask_for_subscription_republish has : recreating subscription");
131
150
  return repair_client_session_by_recreating_a_new_session(session._client!, session, callback);
132
151
  }
133
- // xx assert(session.getPublishEngine().nbPendingPublishRequests === 0);
134
- session.resumePublishEngine();
152
+
135
153
  callback(err);
136
154
  });
137
155
  }
@@ -141,18 +159,16 @@ function create_session_and_repeat_if_failed(
141
159
  session: ClientSessionImpl,
142
160
  callback: CallbackT<ClientSessionImpl>
143
161
  ) {
144
- if (session.hasBeenClosed()) {
145
- doDebug && debugLog("Cannot complete subscription republish due to session termination");
146
- return callback(new Error("Cannot complete subscription republish due to session termination"));
147
- }
162
+ // prettier-ignore
163
+ { const err = _shouldNotContinue(session); if (err) { return callback(err); } }
164
+
148
165
  doDebug && debugLog(chalk.bgWhite.red(" => creating a new session ...."));
149
166
  // create new session, based on old session,
150
167
  // so we can reuse subscriptions data
151
168
  client.__createSession_step2(session, (err: Error | null, session1?: ClientSessionImpl) => {
152
- if (err && session.hasBeenClosed()) {
153
- doDebug && debugLog("Cannot complete subscription republish due to session termination");
154
- return callback(new Error("Cannot complete subscription republish due to session termination"));
155
- }
169
+ // prettier-ignore
170
+ { const err = _shouldNotContinue(session); if (err) { return callback(err); } }
171
+
156
172
  if (!err && session1) {
157
173
  assert(session === session1, "session should have been recycled");
158
174
  callback(err, session);
@@ -169,9 +185,9 @@ function repair_client_session_by_recreating_a_new_session(
169
185
  session: ClientSessionImpl,
170
186
  callback: (err?: Error) => void
171
187
  ) {
172
- if (session._client && session._client.isUnusable()) {
173
- return callback(new Error("Client is unusable"));
174
- }
188
+ // prettier-ignore
189
+ { const err = _shouldNotContinue(session); if (err) { return callback(err); } }
190
+
175
191
  // As we don"t know if server has been rebooted or not,
176
192
  // and may be upgraded in between, we have to invalidate the extra data type manager
177
193
  invalidateExtraDataTypeManager(session);
@@ -181,13 +197,6 @@ function repair_client_session_by_recreating_a_new_session(
181
197
  debugLog(" repairing client session by_recreating a new session for old session ", session.sessionId.toString());
182
198
  }
183
199
 
184
- // TO DO : it is possible that session is already closed while we get there
185
- if (session.hasBeenClosed()) {
186
- // istanbul ignore next
187
- doDebug && debugLog(chalk.bgWhite.red("Aborting reactivation of old session because user requested session to be closed"));
188
- return callback(new Error("reconnection cancelled due to session termination"));
189
- }
190
-
191
200
  let newSession: ClientSessionImpl;
192
201
 
193
202
  const listenerCountBefore = session.listenerCount("");
@@ -196,27 +205,37 @@ function repair_client_session_by_recreating_a_new_session(
196
205
  async.forEach(
197
206
  subscriptionsToRecreate,
198
207
  (subscriptionId: SubscriptionId, next: ErrorCallback) => {
208
+ // prettier-ignore
209
+ { const err = _shouldNotContinue(session); if (err) { return next(err); } }
210
+
199
211
  if (!session.getPublishEngine().hasSubscription(subscriptionId)) {
200
212
  doDebug && debugLog(chalk.red(" => CANNOT RECREATE SUBSCRIPTION "), subscriptionId);
201
213
  return next();
202
214
  }
203
215
  const subscription = session.getPublishEngine().getSubscription(subscriptionId);
204
- assert(subscription.constructor.name === "ClientSubscriptionImpl");
205
216
  doDebug && debugLog(chalk.red(" => RECREATING SUBSCRIPTION "), subscriptionId);
206
- assert(subscription.session === newSession, "must have the session");
207
-
208
- (subscription as ClientSubscriptionImpl).recreateSubscriptionAndMonitoredItem((err1?: Error) => {
209
- if (err1) {
210
- doDebug && debugLog("_recreateSubscription failed !" + err1.message);
211
- }
217
+ assert(subscription.session === newSession, "must have the new session");
212
218
 
219
+ recreateSubscriptionAndMonitoredItem(subscription).then(() => {
213
220
  doDebug &&
214
- debugLog(chalk.cyan(" => RECREATING SUBSCRIPTION AND MONITORED ITEM DONE "), subscriptionId);
221
+ debugLog(
222
+ chalk.cyan(" => RECREATING SUBSCRIPTION AND MONITORED ITEM DONE subscriptionId="),
223
+ subscriptionId
224
+ );
225
+ next();
215
226
 
227
+ }).catch((err) => {
228
+ doDebug && debugLog("_recreateSubscription failed !" + (err as Error).message);
216
229
  next();
217
230
  });
231
+
218
232
  },
219
233
  (err1?: Error | null) => {
234
+ // prettier-ignore
235
+ { const err = _shouldNotContinue(session); if (err) { return innerCallback(err); } }
236
+ if (!err1) {
237
+ // prettier-ignore
238
+ }
220
239
  innerCallback(err1!);
221
240
  }
222
241
  );
@@ -225,9 +244,9 @@ function repair_client_session_by_recreating_a_new_session(
225
244
  async.series(
226
245
  [
227
246
  function suspend_old_session_publish_engine(innerCallback: ErrorCallback) {
228
- if (session.hasBeenClosed()) {
229
- return innerCallback(new Error("Cannot complete subscription republish due to session termination"));
230
- }
247
+ // prettier-ignore
248
+ { const err = _shouldNotContinue(session); if (err) { return innerCallback(err); } }
249
+
231
250
  // istanbul ignore next
232
251
  doDebug && debugLog(chalk.bgWhite.red(" => suspend old session publish engine...."));
233
252
  session.getPublishEngine().suspend(true);
@@ -236,6 +255,9 @@ function repair_client_session_by_recreating_a_new_session(
236
255
 
237
256
  function create_new_session(innerCallback: ErrorCallback) {
238
257
  create_session_and_repeat_if_failed(client, session, (err?: Error | null, _newSession?: ClientSessionImpl) => {
258
+ // prettier-ignore
259
+ { const err = _shouldNotContinue(session); if (err) { return innerCallback(err); } }
260
+
239
261
  if (_newSession) {
240
262
  newSession = _newSession;
241
263
  }
@@ -244,9 +266,9 @@ function repair_client_session_by_recreating_a_new_session(
244
266
  },
245
267
 
246
268
  function activate_new_session(innerCallback: ErrorCallback) {
247
- if (session.hasBeenClosed()) {
248
- return innerCallback(new Error("Cannot complete subscription republish due to session termination"));
249
- }
269
+ // prettier-ignore
270
+ { const err = _shouldNotContinue(session); if (err) { return innerCallback(err); } }
271
+
250
272
  doDebug && debugLog(chalk.bgWhite.red(" => activating a new session ...."));
251
273
 
252
274
  client._activateSession(
@@ -285,9 +307,9 @@ function repair_client_session_by_recreating_a_new_session(
285
307
  },
286
308
 
287
309
  function attempt_subscription_transfer(innerCallback: ErrorCallback) {
288
- if (session.hasBeenClosed()) {
289
- return innerCallback(new Error("Cannot complete subscription republish due to session termination"));
290
- }
310
+ // prettier-ignore
311
+ { const err = _shouldNotContinue(session); if (err) { return innerCallback(err); } }
312
+
291
313
  // get the old subscriptions id from the old session
292
314
  const subscriptionsIds = session.getPublishEngine().getSubscriptionIds();
293
315
 
@@ -310,8 +332,11 @@ function repair_client_session_by_recreating_a_new_session(
310
332
  newSession.transferSubscriptions(
311
333
  subscriptionsToTransfer,
312
334
  (err: Error | null, transferSubscriptionsResponse?: TransferSubscriptionsResponse) => {
335
+ // may be the connection with server has been disconnected
336
+ // prettier-ignore
337
+ { const err = _shouldNotContinue(session); if (err) { return innerCallback(err); } }
338
+
313
339
  if (err || !transferSubscriptionsResponse) {
314
- warningLog(chalk.bgCyan("Warning TransferSubscription has failed " + err?.message));
315
340
  warningLog(chalk.bgCyan("May be the server is not supporting this feature"));
316
341
  // when transfer subscription has failed, we have no other choice but
317
342
  // recreate the subscriptions on the server side
@@ -367,9 +392,9 @@ function repair_client_session_by_recreating_a_new_session(
367
392
  },
368
393
 
369
394
  function ask_for_subscription_republish(innerCallback: ErrorCallback) {
370
- if (session.hasBeenClosed()) {
371
- return innerCallback(new Error("Cannot complete subscription republish due to session termination"));
372
- }
395
+ // prettier-ignore
396
+ { const err = _shouldNotContinue(session); if (err) { return innerCallback(err); } }
397
+
373
398
  // assert(newSession.getPublishEngine().nbPendingPublishRequests === 0, "we should not be publishing here");
374
399
  // call Republish
375
400
  return _ask_for_subscription_republish(newSession, (err) => {
@@ -381,10 +406,11 @@ function repair_client_session_by_recreating_a_new_session(
381
406
  },
382
407
 
383
408
  function start_publishing_as_normal(innerCallback: ErrorCallback) {
384
- if (session.hasBeenClosed()) {
385
- return innerCallback(new Error("Cannot complete subscription republish due to session termination"));
386
- }
409
+ // prettier-ignore
410
+ { const err = _shouldNotContinue(session); if (err) { return innerCallback(err); } }
411
+
387
412
  newSession.getPublishEngine().suspend(false);
413
+
388
414
  const listenerCountAfter = session.listenerCount("");
389
415
  assert(newSession === session);
390
416
  doDebug && debugLog("listenerCountBefore =", listenerCountBefore, "listenerCountAfter = ", listenerCountAfter);
@@ -404,6 +430,8 @@ function _repair_client_session(client: IClientBase, session: ClientSessionImpl,
404
430
  debugLog("Session repair completed with err: ", err2 ? err2.message : "<no error>", session.sessionId.toString());
405
431
  if (!err2) {
406
432
  session.emit("session_repaired");
433
+ } else {
434
+ session.emit("session_repaired_failed", err2);
407
435
  }
408
436
  callback(err2);
409
437
  };
@@ -412,7 +440,13 @@ function _repair_client_session(client: IClientBase, session: ClientSessionImpl,
412
440
  doDebug && debugLog(chalk.yellow(" TRYING TO REACTIVATE EXISTING SESSION"), session.sessionId.toString());
413
441
  doDebug && debugLog(" SubscriptionIds :", session.getPublishEngine().getSubscriptionIds());
414
442
  }
443
+
444
+ // prettier-ignore
445
+ { const err = _shouldNotContinue(session); if (err) { return callback(err); } }
446
+
415
447
  client._activateSession(session, session.userIdentityInfo!, (err: Error | null, session2?: ClientSessionImpl) => {
448
+ // prettier-ignore
449
+ { const err = _shouldNotContinue(session); if (err) { return callback(err); } }
416
450
  //
417
451
  // Note: current limitation :
418
452
  // - The reconnection doesn't work yet, if connection break is caused by a server that crashes and restarts.
@@ -435,14 +469,18 @@ export function repair_client_session(client: IClientBase, session: ClientSessio
435
469
  doDebug && debugLog("Aborting reactivation of old session because user requested session to be close");
436
470
  return callback();
437
471
  }
438
-
439
472
  doDebug && debugLog(chalk.yellow("Starting client session repair"));
440
473
 
441
474
  const privateSession = session as any as Reconnectable;
442
475
  privateSession._reconnecting = privateSession._reconnecting || { reconnecting: false, pendingCallbacks: [] };
443
476
 
477
+ if (session.hasBeenClosed()) {
478
+ privateSession._reconnecting.reconnecting = false;
479
+ doDebug && debugLog("Aborting reactivation of old session because session has been closed");
480
+ return callback();
481
+ }
444
482
  if (privateSession._reconnecting.reconnecting) {
445
- doDebug && debugLog(chalk.bgCyan("Reconnecting already happening for session"), session.sessionId.toString());
483
+ doDebug && debugLog(chalk.bgCyan("Reconnection is already happening for session"), session.sessionId.toString());
446
484
  privateSession._reconnecting.pendingCallbacks.push(callback);
447
485
  return;
448
486
  }
@@ -453,7 +491,13 @@ export function repair_client_session(client: IClientBase, session: ClientSessio
453
491
  const transactionQueue = privateSession.pendingTransactions ? privateSession.pendingTransactions.splice(0) : [];
454
492
 
455
493
  const repeatedAction = (callback: EmptyCallback) => {
494
+ // prettier-ignore
495
+ { const err = _shouldNotContinue(session); if (err) { return callback(err); } }
496
+
456
497
  _repair_client_session(client, session, (err) => {
498
+ // prettier-ignore
499
+ { const err = _shouldNotContinue(session); if (err) { return callback(err); } }
500
+
457
501
  if (err) {
458
502
  errorLog(
459
503
  chalk.red("session restoration has failed! err ="),
@@ -471,32 +515,30 @@ export function repair_client_session(client: IClientBase, session: ClientSessio
471
515
  return;
472
516
  } else {
473
517
  errorLog(chalk.red("session restoration should be interrupted because session has been closed forcefully"));
474
- // session does not need to be repaired anymore
475
- callback();
476
518
  }
519
+ // session does not need to be repaired anymore
520
+ callback();
477
521
  return;
478
522
  }
479
523
 
480
- privateSession._reconnecting.reconnecting = false;
481
-
482
524
  // istanbul ignore next
483
525
  doDebug && debugLog(chalk.yellow("session has been restored"), session.sessionId.toString());
484
-
485
526
  session.emit("session_restored");
486
- const otherCallbacks = privateSession._reconnecting.pendingCallbacks;
487
- privateSession._reconnecting.pendingCallbacks = [];
488
-
489
- // re-inject element in queue
490
-
491
- // istanbul ignore next
492
- doDebug && debugLog(chalk.yellow("re-injecting transaction queue"), transactionQueue.length);
493
-
494
- transactionQueue.forEach((e: any) => privateSession.pendingTransactions.push(e));
495
- otherCallbacks.forEach((c: EmptyCallback) => c(err));
496
527
  callback(err);
497
528
  });
498
529
  };
499
- repeatedAction(callback);
530
+ repeatedAction((err) => {
531
+ privateSession._reconnecting.reconnecting = false;
532
+
533
+ const otherCallbacks = privateSession._reconnecting.pendingCallbacks;
534
+ privateSession._reconnecting.pendingCallbacks = [];
535
+ // re-inject element in queue
536
+ // istanbul ignore next
537
+ doDebug && debugLog(chalk.yellow("re-injecting transaction queue"), transactionQueue.length);
538
+ transactionQueue.forEach((e: any) => privateSession.pendingTransactions.push(e));
539
+ otherCallbacks.forEach((c: EmptyCallback) => c(err));
540
+ callback(err);
541
+ });
500
542
  }
501
543
 
502
544
  export function repair_client_sessions(client: IClientBase, callback: (err?: Error) => void): void {
@@ -512,6 +554,9 @@ export function repair_client_sessions(client: IClientBase, callback: (err?: Err
512
554
  },
513
555
  (err, allErrors: (undefined | Error | null)[] | undefined) => {
514
556
  err && errorLog("sessions reactivation completed with err: err ", err ? err.message : "null");
557
+ // prettier-ignore
558
+ { const err = _shouldNotContinue3(client); if (err) { return callback(err); } }
559
+
515
560
  return callback(err!);
516
561
  }
517
562
  );