node-opcua-client 2.73.1 → 2.76.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 (83) hide show
  1. package/LICENSE +3 -1
  2. package/README.md +54 -0
  3. package/dist/alarms_and_conditions/client_alarm.d.ts +49 -45
  4. package/dist/alarms_and_conditions/client_alarm.js +109 -101
  5. package/dist/alarms_and_conditions/client_alarm.js.map +1 -1
  6. package/dist/alarms_and_conditions/client_alarm_list.d.ts +25 -25
  7. package/dist/alarms_and_conditions/client_alarm_list.js +97 -97
  8. package/dist/alarms_and_conditions/client_alarm_list.js.map +1 -1
  9. package/dist/alarms_and_conditions/client_alarm_tools.d.ts +4 -4
  10. package/dist/alarms_and_conditions/client_alarm_tools.js +156 -152
  11. package/dist/alarms_and_conditions/client_alarm_tools.js.map +1 -1
  12. package/dist/alarms_and_conditions/client_alarm_tools_acknowledge_all_conditions.d.ts +18 -18
  13. package/dist/alarms_and_conditions/client_alarm_tools_acknowledge_all_conditions.js +192 -190
  14. package/dist/alarms_and_conditions/client_alarm_tools_acknowledge_all_conditions.js.map +1 -1
  15. package/dist/alarms_and_conditions/client_alarm_tools_dump_event.d.ts +9 -9
  16. package/dist/alarms_and_conditions/client_alarm_tools_dump_event.js +73 -73
  17. package/dist/alarms_and_conditions/client_alarm_tools_extractConditionFields.d.ts +3 -8
  18. package/dist/alarms_and_conditions/client_alarm_tools_extractConditionFields.js +118 -120
  19. package/dist/alarms_and_conditions/client_alarm_tools_extractConditionFields.js.map +1 -1
  20. package/dist/alarms_and_conditions/client_tools.d.ts +7 -7
  21. package/dist/alarms_and_conditions/client_tools.js +180 -180
  22. package/dist/client_base.d.ts +277 -277
  23. package/dist/client_base.js +14 -14
  24. package/dist/client_monitored_item.d.ts +21 -21
  25. package/dist/client_monitored_item.js +11 -11
  26. package/dist/client_monitored_item_base.d.ts +39 -39
  27. package/dist/client_monitored_item_base.js +2 -2
  28. package/dist/client_monitored_item_group.d.ts +18 -18
  29. package/dist/client_monitored_item_group.js +13 -13
  30. package/dist/client_monitored_item_toolbox.d.ts +15 -15
  31. package/dist/client_monitored_item_toolbox.js +117 -117
  32. package/dist/client_session.d.ts +484 -484
  33. package/dist/client_session.js +10 -10
  34. package/dist/client_session_keepalive_manager.d.ts +34 -34
  35. package/dist/client_session_keepalive_manager.js +150 -150
  36. package/dist/client_subscription.d.ts +241 -241
  37. package/dist/client_subscription.js +14 -14
  38. package/dist/client_utils.d.ts +22 -22
  39. package/dist/client_utils.js +88 -88
  40. package/dist/common.d.ts +17 -17
  41. package/dist/common.js +5 -5
  42. package/dist/index.d.ts +55 -55
  43. package/dist/index.js +93 -93
  44. package/dist/opcua_client.d.ts +132 -132
  45. package/dist/opcua_client.js +33 -33
  46. package/dist/private/client_base_impl.d.ts +157 -157
  47. package/dist/private/client_base_impl.js +1337 -1337
  48. package/dist/private/client_monitored_item_base_impl.d.ts +3 -3
  49. package/dist/private/client_monitored_item_base_impl.js +4 -4
  50. package/dist/private/client_monitored_item_group_impl.d.ts +53 -53
  51. package/dist/private/client_monitored_item_group_impl.js +159 -159
  52. package/dist/private/client_monitored_item_impl.d.ts +93 -93
  53. package/dist/private/client_monitored_item_impl.js +305 -304
  54. package/dist/private/client_monitored_item_impl.js.map +1 -1
  55. package/dist/private/client_publish_engine.d.ts +62 -62
  56. package/dist/private/client_publish_engine.js +408 -408
  57. package/dist/private/client_session_impl.d.ts +621 -621
  58. package/dist/private/client_session_impl.js +1302 -1301
  59. package/dist/private/client_session_impl.js.map +1 -1
  60. package/dist/private/client_subscription_impl.d.ts +93 -93
  61. package/dist/private/client_subscription_impl.js +748 -743
  62. package/dist/private/client_subscription_impl.js.map +1 -1
  63. package/dist/private/i_private_client.d.ts +18 -18
  64. package/dist/private/i_private_client.js +2 -2
  65. package/dist/private/opcua_client_impl.d.ts +169 -169
  66. package/dist/private/opcua_client_impl.js +981 -981
  67. package/dist/reconnection.d.ts +9 -9
  68. package/dist/reconnection.js +408 -408
  69. package/dist/tools/findservers.d.ts +20 -20
  70. package/dist/tools/findservers.js +61 -61
  71. package/dist/tools/read_history_server_capabilities.d.ts +6 -6
  72. package/dist/tools/read_history_server_capabilities.js +99 -99
  73. package/dist/verify.d.ts +5 -5
  74. package/dist/verify.js +134 -134
  75. package/package.json +48 -48
  76. package/source/alarms_and_conditions/client_alarm.ts +28 -17
  77. package/source/alarms_and_conditions/client_alarm_list.ts +1 -0
  78. package/source/alarms_and_conditions/client_alarm_tools.ts +33 -23
  79. package/source/alarms_and_conditions/client_alarm_tools_acknowledge_all_conditions.ts +10 -4
  80. package/source/alarms_and_conditions/client_alarm_tools_extractConditionFields.ts +11 -11
  81. package/source/private/client_monitored_item_impl.ts +4 -3
  82. package/source/private/client_session_impl.ts +10 -2
  83. package/source/private/client_subscription_impl.ts +6 -2
@@ -1,409 +1,409 @@
1
- "use strict";
2
- /**
3
- * @module node-opcua-client-private
4
- */
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.repair_client_sessions = exports.repair_client_session = void 0;
7
- // tslint:disable:only-arrow-functions
8
- const async = require("async");
9
- const chalk = require("chalk");
10
- const node_opcua_assert_1 = require("node-opcua-assert");
11
- const node_opcua_debug_1 = require("node-opcua-debug");
12
- const node_opcua_service_subscription_1 = require("node-opcua-service-subscription");
13
- const node_opcua_status_code_1 = require("node-opcua-status-code");
14
- const node_opcua_types_1 = require("node-opcua-types");
15
- const debugLog = (0, node_opcua_debug_1.make_debugLog)(__filename);
16
- const doDebug = (0, node_opcua_debug_1.checkDebugFlag)(__filename);
17
- const errorLog = (0, node_opcua_debug_1.make_errorLog)(__filename);
18
- const warningLog = (0, node_opcua_debug_1.make_warningLog)(__filename);
19
- //
20
- // a new secure channel has be created, we need to reactivate the corresponding session,
21
- // and reestablish the subscription and restart the publish engine.
22
- //
23
- //
24
- // see OPC UA part 4 ( version 1.03 ) figure 34 page 106
25
- // 6.5 Reestablishing subscription....
26
- //
27
- //
28
- //
29
- // +---------------------+
30
- // | CreateSecureChannel |
31
- // | CreateSession |
32
- // | ActivateSession |
33
- // +---------------------+
34
- // |
35
- // |
36
- // v
37
- // +---------------------+
38
- // | CreateSubscription |<-------------------------------------------------------------+
39
- // +---------------------+ |
40
- // | (1)
41
- // |
42
- // v
43
- // +---------------------+
44
- // (2)------------->| StartPublishEngine |
45
- // +---------------------+
46
- // |
47
- // V
48
- // +---------------------+
49
- // +------->| Monitor Connection |
50
- // | +---------------------+
51
- // | |
52
- // | v
53
- // | Good / \
54
- // +-----------------/ SR? \______Broken_____+
55
- // \ / |
56
- // \ / |
57
- // |
58
- // v
59
- // +---------------------+
60
- // | |
61
- // | CreateSecureChannel |<-----+
62
- // | | |
63
- // +---------------------+ |
64
- // | |
65
- // v |
66
- // / \ |
67
- // / SR? \______Bad________+
68
- // \ /
69
- // \ /
70
- // |
71
- // |Good
72
- // v
73
- // +---------------------+
74
- // | |
75
- // | ActivateSession |
76
- // | |
77
- // +---------------------+
78
- // |
79
- // +----------------------+
80
- // |
81
- // v +-------------------+ +----------------------+
82
- // / \ | CreateSession | | |
83
- // / SR? \______Bad_______>| ActivateSession |-----> | TransferSubscription |
84
- // \ / | | | | (1)
85
- // \ / +-------------------+ +----------------------+ ^
86
- // | Good | |
87
- // v (for each subscription) | |
88
- // +--------------------+ / \ |
89
- // | | OK / OK? \______Bad________+
90
- // | RePublish |<----------------------------------------- \ /
91
- // +-->| | \ /
92
- // | +--------------------+
93
- // | |
94
- // | v
95
- // | GOOD / \
96
- // +------ / SR? \______Bad SubscriptionInvalidId______>(1)
97
- // (2) \ /
98
- // ^ \ /
99
- // | |
100
- // | |
101
- // | BadMessageNotAvailable |
102
- // +-------------------------------+
103
- function _ask_for_subscription_republish(session, callback) {
104
- if (session.hasBeenClosed()) {
105
- debugLog("_ask_for_subscription_republish : session is closed");
106
- return callback(new Error("askForSubscriptionRepublish => canceled because session is closed"));
107
- }
108
- debugLog(chalk.bgCyan.yellow.bold("_ask_for_subscription_republish "));
109
- // assert(session.getPublishEngine().nbPendingPublishRequests === 0,
110
- // "at this time, publish request queue shall still be empty");
111
- session.getPublishEngine().republish((err) => {
112
- debugLog("_ask_for_subscription_republish : republish sent");
113
- if (session.hasBeenClosed()) {
114
- return callback(new Error("Cannot complete subscription republish due to session termination"));
115
- }
116
- debugLog(chalk.bgCyan.green.bold("_ask_for_subscription_republish done "), err ? err.message : "OK");
117
- if (err) {
118
- debugLog("_ask_for_subscription_republish has : recreating subscription");
119
- return repair_client_session_by_recreating_a_new_session(session._client, session, callback);
120
- }
121
- // xx assert(session.getPublishEngine().nbPendingPublishRequests === 0);
122
- session.resumePublishEngine();
123
- callback(err);
124
- });
125
- }
126
- function create_session_and_repeat_if_failed_Old(client, session, callback) {
127
- if (session.hasBeenClosed()) {
128
- return callback(new Error("Cannot complete subscription republish due to session termination"));
129
- }
130
- debugLog(chalk.bgWhite.red(" => creating a new session ...."));
131
- // create new session, based on old session,
132
- // so we can reuse subscriptions data
133
- client.__createSession_step2(session, (err, session1) => {
134
- if (err && session.hasBeenClosed()) {
135
- return callback(new Error("Cannot complete subscription republish due to session termination"));
136
- }
137
- if (!err && session1) {
138
- const newSession = session1;
139
- (0, node_opcua_assert_1.assert)(session === session1, "session should have been recycled");
140
- callback(err, newSession);
141
- return;
142
- }
143
- else {
144
- debugLog(chalk.bgWhite.cyan(" => creating a new session (based on old session data).... Done"));
145
- setTimeout(() => {
146
- create_session_and_repeat_if_failed(client, session, callback);
147
- }, 1000);
148
- return;
149
- }
150
- });
151
- }
152
- function create_session_and_repeat_if_failed(client, session, callback) {
153
- if (session.hasBeenClosed()) {
154
- debugLog("Cannot complete subscription republish due to session terminatio");
155
- return callback(new Error("Cannot complete subscription republish due to session termination"));
156
- }
157
- debugLog(chalk.bgWhite.red(" => creating a new session ...."));
158
- // create new session, based on old session,
159
- // so we can reuse subscriptions data
160
- client.__createSession_step2(session, (err, session1) => {
161
- if (err && session.hasBeenClosed()) {
162
- debugLog("Cannot complete subscription republish due to session terminatio");
163
- return callback(new Error("Cannot complete subscription republish due to session termination"));
164
- }
165
- if (!err && session1) {
166
- const newSession = session1;
167
- (0, node_opcua_assert_1.assert)(session === session1, "session should have been recycled");
168
- callback(err, newSession);
169
- return;
170
- }
171
- else {
172
- debugLog("Cannot complete subscription republish err = ", err === null || err === void 0 ? void 0 : err.message);
173
- callback(err);
174
- }
175
- });
176
- }
177
- function repair_client_session_by_recreating_a_new_session(client, session, callback) {
178
- if (doDebug) {
179
- debugLog(" repairing client session by_recreating a new session for old session ", session.sessionId.toString());
180
- }
181
- // TO DO : it is possible that session is already closed while we get there
182
- if (session.hasBeenClosed()) {
183
- debugLog(chalk.bgWhite.red("Aborting reactivation of old session because user requested session to be closed"));
184
- return callback(new Error("reconnection cancelled due to session termination"));
185
- }
186
- let newSession;
187
- const listenerCountBefore = session.listenerCount("");
188
- async.series([
189
- function suspend_old_session_publish_engine(innerCallback) {
190
- if (session.hasBeenClosed()) {
191
- return innerCallback(new Error("Cannot complete subscription republish due to session termination"));
192
- }
193
- debugLog(chalk.bgWhite.red(" => suspend old session publish engine...."));
194
- session.getPublishEngine().suspend(true);
195
- innerCallback();
196
- },
197
- function create_new_session(innerCallback) {
198
- create_session_and_repeat_if_failed(client, session, (err, _newSession) => {
199
- if (_newSession) {
200
- newSession = _newSession;
201
- }
202
- innerCallback(err || undefined);
203
- });
204
- },
205
- function activate_new_session(innerCallback) {
206
- if (session.hasBeenClosed()) {
207
- return innerCallback(new Error("Cannot complete subscription republish due to session termination"));
208
- }
209
- debugLog(chalk.bgWhite.red(" => activating a new session ...."));
210
- client._activateSession(newSession, (err, session1) => {
211
- debugLog(chalk.bgWhite.cyan(" => activating a new session .... Done err=", err ? err.message : "null"));
212
- if (err) {
213
- debugLog(chalk.bgWhite.cyan("reactivation of the new session has failed: let be smart and close it before failing this repair attempt"));
214
- // but just on the server side, not on the client side
215
- const closeSessionRequest = new node_opcua_types_1.CloseSessionRequest({
216
- deleteSubscriptions: true
217
- });
218
- session.performMessageTransaction(closeSessionRequest, (err2) => {
219
- if (err2) {
220
- warningLog("closing session", err2.message);
221
- }
222
- innerCallback(err);
223
- });
224
- }
225
- else {
226
- innerCallback(err ? err : undefined);
227
- }
228
- });
229
- },
230
- function attempt_subscription_transfer(innerCallback) {
231
- if (session.hasBeenClosed()) {
232
- return innerCallback(new Error("Cannot complete subscription republish due to session termination"));
233
- }
234
- // get the old subscriptions id from the old session
235
- const subscriptionsIds = session.getPublishEngine().getSubscriptionIds();
236
- debugLog(" session subscriptionCount = ", newSession.getPublishEngine().subscriptionCount);
237
- if (subscriptionsIds.length === 0) {
238
- debugLog(" No subscriptions => skipping transfer subscriptions");
239
- return innerCallback(); // no need to transfer subscriptions
240
- }
241
- debugLog(" => asking server to transfer subscriptions = [", subscriptionsIds.join(", "), "]");
242
- // Transfer subscriptions - ask for initial values....
243
- const subscriptionsToTransfer = new node_opcua_service_subscription_1.TransferSubscriptionsRequest({
244
- sendInitialValues: true,
245
- subscriptionIds: subscriptionsIds
246
- });
247
- if (newSession.getPublishEngine().nbPendingPublishRequests !== 0) {
248
- warningLog("Warning : we should not be publishing here");
249
- }
250
- newSession.transferSubscriptions(subscriptionsToTransfer, (err, transferSubscriptionsResponse) => {
251
- if (err) {
252
- warningLog(chalk.bgCyan("Warning TransferSubscription has failed " + err.message));
253
- warningLog(chalk.bgCyan("May be the server is not supporting this feature"));
254
- // when transfer subscription has failed, we have no other choice but
255
- // recreate the subscriptions on the server side
256
- return innerCallback();
257
- }
258
- // istanbul ignore next
259
- if (!transferSubscriptionsResponse) {
260
- return innerCallback(new Error("Internal Error"));
261
- }
262
- const results = transferSubscriptionsResponse.results || [];
263
- // istanbul ignore next
264
- if (doDebug) {
265
- debugLog(chalk.cyan(" => transfer subscriptions done"), results.map((x) => x.statusCode.toString()).join(" "));
266
- }
267
- const subscriptionsToRecreate = [];
268
- // some subscriptions may be marked as invalid on the server side ...
269
- // those one need to be recreated and repaired ....
270
- for (let i = 0; i < results.length; i++) {
271
- const statusCode = results[i].statusCode;
272
- if (statusCode === node_opcua_status_code_1.StatusCodes.BadSubscriptionIdInvalid) {
273
- // repair subscription
274
- debugLog(chalk.red(" WARNING SUBSCRIPTION "), subscriptionsIds[i], chalk.red(" SHOULD BE RECREATED"));
275
- subscriptionsToRecreate.push(subscriptionsIds[i]);
276
- }
277
- else {
278
- const availableSequenceNumbers = results[i].availableSequenceNumbers;
279
- debugLog(chalk.green(" SUBSCRIPTION "), subscriptionsIds[i], chalk.green(" CAN BE REPAIRED AND AVAILABLE "), availableSequenceNumbers);
280
- // should be Good.
281
- }
282
- }
283
- debugLog(" new session subscriptionCount = ", newSession.getPublishEngine().subscriptionCount);
284
- async.forEach(subscriptionsToRecreate, (subscriptionId, next) => {
285
- if (!session.getPublishEngine().hasSubscription(subscriptionId)) {
286
- debugLog(chalk.red(" => CANNOT RECREATE SUBSCRIPTION "), subscriptionId);
287
- return next();
288
- }
289
- const subscription = session.getPublishEngine().getSubscription(subscriptionId);
290
- (0, node_opcua_assert_1.assert)(subscription.constructor.name === "ClientSubscriptionImpl");
291
- debugLog(chalk.red(" => RECREATING SUBSCRIPTION "), subscriptionId);
292
- (0, node_opcua_assert_1.assert)(subscription.session === newSession, "must have the session");
293
- subscription.recreateSubscriptionAndMonitoredItem((err1) => {
294
- if (err1) {
295
- debugLog("_recreateSubscription failed !" + err1.message);
296
- }
297
- debugLog(chalk.cyan(" => RECREATING SUBSCRIPTION AND MONITORED ITEM DONE "), subscriptionId);
298
- next();
299
- });
300
- }, (err1) => {
301
- innerCallback(err1);
302
- });
303
- });
304
- },
305
- function ask_for_subscription_republish(innerCallback) {
306
- if (session.hasBeenClosed()) {
307
- return innerCallback(new Error("Cannot complete subscription republish due to session termination"));
308
- }
309
- // assert(newSession.getPublishEngine().nbPendingPublishRequests === 0, "we should not be publishing here");
310
- // call Republish
311
- return _ask_for_subscription_republish(newSession, (err) => {
312
- if (err) {
313
- // tslint:disable-next-line: no-console
314
- console.log("warning: Subscription republished has failed ", err.message);
315
- }
316
- innerCallback(err);
317
- });
318
- },
319
- function start_publishing_as_normal(innerCallback) {
320
- if (session.hasBeenClosed()) {
321
- return innerCallback(new Error("Cannot complete subscription republish due to session termination"));
322
- }
323
- newSession.getPublishEngine().suspend(false);
324
- const listenerCountAfter = session.listenerCount("");
325
- (0, node_opcua_assert_1.assert)(newSession === session);
326
- debugLog("listenerCountBefore =", listenerCountBefore, "listenerCountAfter = ", listenerCountAfter);
327
- innerCallback();
328
- }
329
- ], (err) => {
330
- callback(err);
331
- });
332
- }
333
- function _repair_client_session(client, session, callback) {
334
- const callback2 = (err2) => {
335
- debugLog("Session repair completed with err: ", err2 ? err2.message : "<no error>", session.sessionId.toString());
336
- session.emit("session_repaired");
337
- callback(err2);
338
- };
339
- if (doDebug) {
340
- debugLog(chalk.yellow(" TRYING TO REACTIVATE EXISTING SESSION"), session.sessionId.toString());
341
- debugLog(" SubscriptionIds :", session.getPublishEngine().getSubscriptionIds());
342
- }
343
- client._activateSession(session, (err, session2) => {
344
- //
345
- // Note: current limitation :
346
- // - The reconnection doesn't work yet, if connection break is caused by a server that crashes and restarts.
347
- //
348
- debugLog(" ActivateSession : ", err ? chalk.red(err.message) : chalk.green(" SUCCESS !!! "));
349
- if (err) {
350
- // activate old session has failed => let's recreate a new Channel and transfer the subscription
351
- return repair_client_session_by_recreating_a_new_session(client, session, callback2);
352
- }
353
- else {
354
- // activate old session has succeeded => let's call Republish
355
- return _ask_for_subscription_republish(session, callback2);
356
- }
357
- });
358
- }
359
- function repair_client_session(client, session, callback) {
360
- if (!client) {
361
- debugLog("Aborting reactivation of old session because user requested session to be close");
362
- return callback();
363
- }
364
- debugLog(chalk.yellow("Starting client session repair"));
365
- const privateSession = session;
366
- privateSession._reconnecting = privateSession._reconnecting || { reconnecting: false, pendingCallbacks: [] };
367
- if (privateSession._reconnecting.reconnecting) {
368
- debugLog(chalk.bgCyan("Reconnecting already happening for session"), session.sessionId.toString());
369
- privateSession._reconnecting.pendingCallbacks.push(callback);
370
- return;
371
- }
372
- privateSession._reconnecting.reconnecting = true;
373
- // get old transaction queue ...
374
- const transactionQueue = privateSession.pendingTransactions ? privateSession.pendingTransactions.splice(0) : [];
375
- _repair_client_session(client, session, (err) => {
376
- privateSession._reconnecting.reconnecting = false;
377
- if (err) {
378
- errorLog(chalk.red("session restoration has failed! err ="), err.message, session.sessionId.toString(), " => Let's retry");
379
- setTimeout(() => {
380
- _repair_client_session(client, session, callback);
381
- }, 2000);
382
- return;
383
- }
384
- debugLog(chalk.yellow("session has been restored"), session.sessionId.toString());
385
- session.emit("session_restored");
386
- const otherCallbacks = privateSession._reconnecting.pendingCallbacks;
387
- privateSession._reconnecting.pendingCallbacks = [];
388
- // re-inject element in queue
389
- debugLog(chalk.yellow("re-injecting transaction queue"), transactionQueue.length);
390
- transactionQueue.forEach((e) => privateSession.pendingTransactions.push(e));
391
- otherCallbacks.forEach((c) => c(err));
392
- callback(err);
393
- });
394
- }
395
- exports.repair_client_session = repair_client_session;
396
- function repair_client_sessions(client, callback) {
397
- const self = client;
398
- // repair session
399
- const sessions = self.getSessions();
400
- debugLog(chalk.red.bgWhite(" Starting sessions reactivation", sessions.length));
401
- async.map(sessions, (session, next) => {
402
- repair_client_session(client, session, next);
403
- }, (err) => {
404
- err && errorLog("sessions reactivation completed: err ", err ? err.message : "null");
405
- return callback(err);
406
- });
407
- }
408
- exports.repair_client_sessions = repair_client_sessions;
1
+ "use strict";
2
+ /**
3
+ * @module node-opcua-client-private
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.repair_client_sessions = exports.repair_client_session = void 0;
7
+ // tslint:disable:only-arrow-functions
8
+ const async = require("async");
9
+ const chalk = require("chalk");
10
+ const node_opcua_assert_1 = require("node-opcua-assert");
11
+ const node_opcua_debug_1 = require("node-opcua-debug");
12
+ const node_opcua_service_subscription_1 = require("node-opcua-service-subscription");
13
+ const node_opcua_status_code_1 = require("node-opcua-status-code");
14
+ const node_opcua_types_1 = require("node-opcua-types");
15
+ const debugLog = (0, node_opcua_debug_1.make_debugLog)(__filename);
16
+ const doDebug = (0, node_opcua_debug_1.checkDebugFlag)(__filename);
17
+ const errorLog = (0, node_opcua_debug_1.make_errorLog)(__filename);
18
+ const warningLog = (0, node_opcua_debug_1.make_warningLog)(__filename);
19
+ //
20
+ // a new secure channel has be created, we need to reactivate the corresponding session,
21
+ // and reestablish the subscription and restart the publish engine.
22
+ //
23
+ //
24
+ // see OPC UA part 4 ( version 1.03 ) figure 34 page 106
25
+ // 6.5 Reestablishing subscription....
26
+ //
27
+ //
28
+ //
29
+ // +---------------------+
30
+ // | CreateSecureChannel |
31
+ // | CreateSession |
32
+ // | ActivateSession |
33
+ // +---------------------+
34
+ // |
35
+ // |
36
+ // v
37
+ // +---------------------+
38
+ // | CreateSubscription |<-------------------------------------------------------------+
39
+ // +---------------------+ |
40
+ // | (1)
41
+ // |
42
+ // v
43
+ // +---------------------+
44
+ // (2)------------->| StartPublishEngine |
45
+ // +---------------------+
46
+ // |
47
+ // V
48
+ // +---------------------+
49
+ // +------->| Monitor Connection |
50
+ // | +---------------------+
51
+ // | |
52
+ // | v
53
+ // | Good / \
54
+ // +-----------------/ SR? \______Broken_____+
55
+ // \ / |
56
+ // \ / |
57
+ // |
58
+ // v
59
+ // +---------------------+
60
+ // | |
61
+ // | CreateSecureChannel |<-----+
62
+ // | | |
63
+ // +---------------------+ |
64
+ // | |
65
+ // v |
66
+ // / \ |
67
+ // / SR? \______Bad________+
68
+ // \ /
69
+ // \ /
70
+ // |
71
+ // |Good
72
+ // v
73
+ // +---------------------+
74
+ // | |
75
+ // | ActivateSession |
76
+ // | |
77
+ // +---------------------+
78
+ // |
79
+ // +----------------------+
80
+ // |
81
+ // v +-------------------+ +----------------------+
82
+ // / \ | CreateSession | | |
83
+ // / SR? \______Bad_______>| ActivateSession |-----> | TransferSubscription |
84
+ // \ / | | | | (1)
85
+ // \ / +-------------------+ +----------------------+ ^
86
+ // | Good | |
87
+ // v (for each subscription) | |
88
+ // +--------------------+ / \ |
89
+ // | | OK / OK? \______Bad________+
90
+ // | RePublish |<----------------------------------------- \ /
91
+ // +-->| | \ /
92
+ // | +--------------------+
93
+ // | |
94
+ // | v
95
+ // | GOOD / \
96
+ // +------ / SR? \______Bad SubscriptionInvalidId______>(1)
97
+ // (2) \ /
98
+ // ^ \ /
99
+ // | |
100
+ // | |
101
+ // | BadMessageNotAvailable |
102
+ // +-------------------------------+
103
+ function _ask_for_subscription_republish(session, callback) {
104
+ if (session.hasBeenClosed()) {
105
+ debugLog("_ask_for_subscription_republish : session is closed");
106
+ return callback(new Error("askForSubscriptionRepublish => canceled because session is closed"));
107
+ }
108
+ debugLog(chalk.bgCyan.yellow.bold("_ask_for_subscription_republish "));
109
+ // assert(session.getPublishEngine().nbPendingPublishRequests === 0,
110
+ // "at this time, publish request queue shall still be empty");
111
+ session.getPublishEngine().republish((err) => {
112
+ debugLog("_ask_for_subscription_republish : republish sent");
113
+ if (session.hasBeenClosed()) {
114
+ return callback(new Error("Cannot complete subscription republish due to session termination"));
115
+ }
116
+ debugLog(chalk.bgCyan.green.bold("_ask_for_subscription_republish done "), err ? err.message : "OK");
117
+ if (err) {
118
+ debugLog("_ask_for_subscription_republish has : recreating subscription");
119
+ return repair_client_session_by_recreating_a_new_session(session._client, session, callback);
120
+ }
121
+ // xx assert(session.getPublishEngine().nbPendingPublishRequests === 0);
122
+ session.resumePublishEngine();
123
+ callback(err);
124
+ });
125
+ }
126
+ function create_session_and_repeat_if_failed_Old(client, session, callback) {
127
+ if (session.hasBeenClosed()) {
128
+ return callback(new Error("Cannot complete subscription republish due to session termination"));
129
+ }
130
+ debugLog(chalk.bgWhite.red(" => creating a new session ...."));
131
+ // create new session, based on old session,
132
+ // so we can reuse subscriptions data
133
+ client.__createSession_step2(session, (err, session1) => {
134
+ if (err && session.hasBeenClosed()) {
135
+ return callback(new Error("Cannot complete subscription republish due to session termination"));
136
+ }
137
+ if (!err && session1) {
138
+ const newSession = session1;
139
+ (0, node_opcua_assert_1.assert)(session === session1, "session should have been recycled");
140
+ callback(err, newSession);
141
+ return;
142
+ }
143
+ else {
144
+ debugLog(chalk.bgWhite.cyan(" => creating a new session (based on old session data).... Done"));
145
+ setTimeout(() => {
146
+ create_session_and_repeat_if_failed(client, session, callback);
147
+ }, 1000);
148
+ return;
149
+ }
150
+ });
151
+ }
152
+ function create_session_and_repeat_if_failed(client, session, callback) {
153
+ if (session.hasBeenClosed()) {
154
+ debugLog("Cannot complete subscription republish due to session terminatio");
155
+ return callback(new Error("Cannot complete subscription republish due to session termination"));
156
+ }
157
+ debugLog(chalk.bgWhite.red(" => creating a new session ...."));
158
+ // create new session, based on old session,
159
+ // so we can reuse subscriptions data
160
+ client.__createSession_step2(session, (err, session1) => {
161
+ if (err && session.hasBeenClosed()) {
162
+ debugLog("Cannot complete subscription republish due to session terminatio");
163
+ return callback(new Error("Cannot complete subscription republish due to session termination"));
164
+ }
165
+ if (!err && session1) {
166
+ const newSession = session1;
167
+ (0, node_opcua_assert_1.assert)(session === session1, "session should have been recycled");
168
+ callback(err, newSession);
169
+ return;
170
+ }
171
+ else {
172
+ debugLog("Cannot complete subscription republish err = ", err === null || err === void 0 ? void 0 : err.message);
173
+ callback(err);
174
+ }
175
+ });
176
+ }
177
+ function repair_client_session_by_recreating_a_new_session(client, session, callback) {
178
+ if (doDebug) {
179
+ debugLog(" repairing client session by_recreating a new session for old session ", session.sessionId.toString());
180
+ }
181
+ // TO DO : it is possible that session is already closed while we get there
182
+ if (session.hasBeenClosed()) {
183
+ debugLog(chalk.bgWhite.red("Aborting reactivation of old session because user requested session to be closed"));
184
+ return callback(new Error("reconnection cancelled due to session termination"));
185
+ }
186
+ let newSession;
187
+ const listenerCountBefore = session.listenerCount("");
188
+ async.series([
189
+ function suspend_old_session_publish_engine(innerCallback) {
190
+ if (session.hasBeenClosed()) {
191
+ return innerCallback(new Error("Cannot complete subscription republish due to session termination"));
192
+ }
193
+ debugLog(chalk.bgWhite.red(" => suspend old session publish engine...."));
194
+ session.getPublishEngine().suspend(true);
195
+ innerCallback();
196
+ },
197
+ function create_new_session(innerCallback) {
198
+ create_session_and_repeat_if_failed(client, session, (err, _newSession) => {
199
+ if (_newSession) {
200
+ newSession = _newSession;
201
+ }
202
+ innerCallback(err || undefined);
203
+ });
204
+ },
205
+ function activate_new_session(innerCallback) {
206
+ if (session.hasBeenClosed()) {
207
+ return innerCallback(new Error("Cannot complete subscription republish due to session termination"));
208
+ }
209
+ debugLog(chalk.bgWhite.red(" => activating a new session ...."));
210
+ client._activateSession(newSession, (err, session1) => {
211
+ debugLog(chalk.bgWhite.cyan(" => activating a new session .... Done err=", err ? err.message : "null"));
212
+ if (err) {
213
+ debugLog(chalk.bgWhite.cyan("reactivation of the new session has failed: let be smart and close it before failing this repair attempt"));
214
+ // but just on the server side, not on the client side
215
+ const closeSessionRequest = new node_opcua_types_1.CloseSessionRequest({
216
+ deleteSubscriptions: true
217
+ });
218
+ session.performMessageTransaction(closeSessionRequest, (err2) => {
219
+ if (err2) {
220
+ warningLog("closing session", err2.message);
221
+ }
222
+ innerCallback(err);
223
+ });
224
+ }
225
+ else {
226
+ innerCallback(err ? err : undefined);
227
+ }
228
+ });
229
+ },
230
+ function attempt_subscription_transfer(innerCallback) {
231
+ if (session.hasBeenClosed()) {
232
+ return innerCallback(new Error("Cannot complete subscription republish due to session termination"));
233
+ }
234
+ // get the old subscriptions id from the old session
235
+ const subscriptionsIds = session.getPublishEngine().getSubscriptionIds();
236
+ debugLog(" session subscriptionCount = ", newSession.getPublishEngine().subscriptionCount);
237
+ if (subscriptionsIds.length === 0) {
238
+ debugLog(" No subscriptions => skipping transfer subscriptions");
239
+ return innerCallback(); // no need to transfer subscriptions
240
+ }
241
+ debugLog(" => asking server to transfer subscriptions = [", subscriptionsIds.join(", "), "]");
242
+ // Transfer subscriptions - ask for initial values....
243
+ const subscriptionsToTransfer = new node_opcua_service_subscription_1.TransferSubscriptionsRequest({
244
+ sendInitialValues: true,
245
+ subscriptionIds: subscriptionsIds
246
+ });
247
+ if (newSession.getPublishEngine().nbPendingPublishRequests !== 0) {
248
+ warningLog("Warning : we should not be publishing here");
249
+ }
250
+ newSession.transferSubscriptions(subscriptionsToTransfer, (err, transferSubscriptionsResponse) => {
251
+ if (err) {
252
+ warningLog(chalk.bgCyan("Warning TransferSubscription has failed " + err.message));
253
+ warningLog(chalk.bgCyan("May be the server is not supporting this feature"));
254
+ // when transfer subscription has failed, we have no other choice but
255
+ // recreate the subscriptions on the server side
256
+ return innerCallback();
257
+ }
258
+ // istanbul ignore next
259
+ if (!transferSubscriptionsResponse) {
260
+ return innerCallback(new Error("Internal Error"));
261
+ }
262
+ const results = transferSubscriptionsResponse.results || [];
263
+ // istanbul ignore next
264
+ if (doDebug) {
265
+ debugLog(chalk.cyan(" => transfer subscriptions done"), results.map((x) => x.statusCode.toString()).join(" "));
266
+ }
267
+ const subscriptionsToRecreate = [];
268
+ // some subscriptions may be marked as invalid on the server side ...
269
+ // those one need to be recreated and repaired ....
270
+ for (let i = 0; i < results.length; i++) {
271
+ const statusCode = results[i].statusCode;
272
+ if (statusCode === node_opcua_status_code_1.StatusCodes.BadSubscriptionIdInvalid) {
273
+ // repair subscription
274
+ debugLog(chalk.red(" WARNING SUBSCRIPTION "), subscriptionsIds[i], chalk.red(" SHOULD BE RECREATED"));
275
+ subscriptionsToRecreate.push(subscriptionsIds[i]);
276
+ }
277
+ else {
278
+ const availableSequenceNumbers = results[i].availableSequenceNumbers;
279
+ debugLog(chalk.green(" SUBSCRIPTION "), subscriptionsIds[i], chalk.green(" CAN BE REPAIRED AND AVAILABLE "), availableSequenceNumbers);
280
+ // should be Good.
281
+ }
282
+ }
283
+ debugLog(" new session subscriptionCount = ", newSession.getPublishEngine().subscriptionCount);
284
+ async.forEach(subscriptionsToRecreate, (subscriptionId, next) => {
285
+ if (!session.getPublishEngine().hasSubscription(subscriptionId)) {
286
+ debugLog(chalk.red(" => CANNOT RECREATE SUBSCRIPTION "), subscriptionId);
287
+ return next();
288
+ }
289
+ const subscription = session.getPublishEngine().getSubscription(subscriptionId);
290
+ (0, node_opcua_assert_1.assert)(subscription.constructor.name === "ClientSubscriptionImpl");
291
+ debugLog(chalk.red(" => RECREATING SUBSCRIPTION "), subscriptionId);
292
+ (0, node_opcua_assert_1.assert)(subscription.session === newSession, "must have the session");
293
+ subscription.recreateSubscriptionAndMonitoredItem((err1) => {
294
+ if (err1) {
295
+ debugLog("_recreateSubscription failed !" + err1.message);
296
+ }
297
+ debugLog(chalk.cyan(" => RECREATING SUBSCRIPTION AND MONITORED ITEM DONE "), subscriptionId);
298
+ next();
299
+ });
300
+ }, (err1) => {
301
+ innerCallback(err1);
302
+ });
303
+ });
304
+ },
305
+ function ask_for_subscription_republish(innerCallback) {
306
+ if (session.hasBeenClosed()) {
307
+ return innerCallback(new Error("Cannot complete subscription republish due to session termination"));
308
+ }
309
+ // assert(newSession.getPublishEngine().nbPendingPublishRequests === 0, "we should not be publishing here");
310
+ // call Republish
311
+ return _ask_for_subscription_republish(newSession, (err) => {
312
+ if (err) {
313
+ // tslint:disable-next-line: no-console
314
+ console.log("warning: Subscription republished has failed ", err.message);
315
+ }
316
+ innerCallback(err);
317
+ });
318
+ },
319
+ function start_publishing_as_normal(innerCallback) {
320
+ if (session.hasBeenClosed()) {
321
+ return innerCallback(new Error("Cannot complete subscription republish due to session termination"));
322
+ }
323
+ newSession.getPublishEngine().suspend(false);
324
+ const listenerCountAfter = session.listenerCount("");
325
+ (0, node_opcua_assert_1.assert)(newSession === session);
326
+ debugLog("listenerCountBefore =", listenerCountBefore, "listenerCountAfter = ", listenerCountAfter);
327
+ innerCallback();
328
+ }
329
+ ], (err) => {
330
+ callback(err);
331
+ });
332
+ }
333
+ function _repair_client_session(client, session, callback) {
334
+ const callback2 = (err2) => {
335
+ debugLog("Session repair completed with err: ", err2 ? err2.message : "<no error>", session.sessionId.toString());
336
+ session.emit("session_repaired");
337
+ callback(err2);
338
+ };
339
+ if (doDebug) {
340
+ debugLog(chalk.yellow(" TRYING TO REACTIVATE EXISTING SESSION"), session.sessionId.toString());
341
+ debugLog(" SubscriptionIds :", session.getPublishEngine().getSubscriptionIds());
342
+ }
343
+ client._activateSession(session, (err, session2) => {
344
+ //
345
+ // Note: current limitation :
346
+ // - The reconnection doesn't work yet, if connection break is caused by a server that crashes and restarts.
347
+ //
348
+ debugLog(" ActivateSession : ", err ? chalk.red(err.message) : chalk.green(" SUCCESS !!! "));
349
+ if (err) {
350
+ // activate old session has failed => let's recreate a new Channel and transfer the subscription
351
+ return repair_client_session_by_recreating_a_new_session(client, session, callback2);
352
+ }
353
+ else {
354
+ // activate old session has succeeded => let's call Republish
355
+ return _ask_for_subscription_republish(session, callback2);
356
+ }
357
+ });
358
+ }
359
+ function repair_client_session(client, session, callback) {
360
+ if (!client) {
361
+ debugLog("Aborting reactivation of old session because user requested session to be close");
362
+ return callback();
363
+ }
364
+ debugLog(chalk.yellow("Starting client session repair"));
365
+ const privateSession = session;
366
+ privateSession._reconnecting = privateSession._reconnecting || { reconnecting: false, pendingCallbacks: [] };
367
+ if (privateSession._reconnecting.reconnecting) {
368
+ debugLog(chalk.bgCyan("Reconnecting already happening for session"), session.sessionId.toString());
369
+ privateSession._reconnecting.pendingCallbacks.push(callback);
370
+ return;
371
+ }
372
+ privateSession._reconnecting.reconnecting = true;
373
+ // get old transaction queue ...
374
+ const transactionQueue = privateSession.pendingTransactions ? privateSession.pendingTransactions.splice(0) : [];
375
+ _repair_client_session(client, session, (err) => {
376
+ privateSession._reconnecting.reconnecting = false;
377
+ if (err) {
378
+ errorLog(chalk.red("session restoration has failed! err ="), err.message, session.sessionId.toString(), " => Let's retry");
379
+ setTimeout(() => {
380
+ _repair_client_session(client, session, callback);
381
+ }, 2000);
382
+ return;
383
+ }
384
+ debugLog(chalk.yellow("session has been restored"), session.sessionId.toString());
385
+ session.emit("session_restored");
386
+ const otherCallbacks = privateSession._reconnecting.pendingCallbacks;
387
+ privateSession._reconnecting.pendingCallbacks = [];
388
+ // re-inject element in queue
389
+ debugLog(chalk.yellow("re-injecting transaction queue"), transactionQueue.length);
390
+ transactionQueue.forEach((e) => privateSession.pendingTransactions.push(e));
391
+ otherCallbacks.forEach((c) => c(err));
392
+ callback(err);
393
+ });
394
+ }
395
+ exports.repair_client_session = repair_client_session;
396
+ function repair_client_sessions(client, callback) {
397
+ const self = client;
398
+ // repair session
399
+ const sessions = self.getSessions();
400
+ debugLog(chalk.red.bgWhite(" Starting sessions reactivation", sessions.length));
401
+ async.map(sessions, (session, next) => {
402
+ repair_client_session(client, session, next);
403
+ }, (err) => {
404
+ err && errorLog("sessions reactivation completed: err ", err ? err.message : "null");
405
+ return callback(err);
406
+ });
407
+ }
408
+ exports.repair_client_sessions = repair_client_sessions;
409
409
  //# sourceMappingURL=reconnection.js.map