node-opcua-server 2.76.0 → 2.77.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 (63) hide show
  1. package/dist/base_server.d.ts +110 -110
  2. package/dist/base_server.js +473 -473
  3. package/dist/factory.d.ts +12 -12
  4. package/dist/factory.js +23 -23
  5. package/dist/factory.js.map +1 -1
  6. package/dist/filter/check_where_clause_on_address_space.d.ts +3 -3
  7. package/dist/filter/check_where_clause_on_address_space.js +22 -22
  8. package/dist/filter/extract_event_fields.d.ts +10 -10
  9. package/dist/filter/extract_event_fields.js +17 -17
  10. package/dist/helper.d.ts +10 -10
  11. package/dist/helper.js +75 -75
  12. package/dist/history_server_capabilities.d.ts +35 -35
  13. package/dist/history_server_capabilities.js +43 -43
  14. package/dist/i_channel_data.d.ts +13 -13
  15. package/dist/i_channel_data.js +2 -2
  16. package/dist/i_register_server_manager.d.ts +16 -16
  17. package/dist/i_register_server_manager.js +2 -2
  18. package/dist/i_server_side_publish_engine.d.ts +36 -36
  19. package/dist/i_server_side_publish_engine.js +49 -49
  20. package/dist/i_socket_data.d.ts +11 -11
  21. package/dist/i_socket_data.js +2 -2
  22. package/dist/index.d.ts +16 -16
  23. package/dist/index.js +32 -32
  24. package/dist/monitored_item.d.ts +177 -177
  25. package/dist/monitored_item.js +1001 -1001
  26. package/dist/node_sampler.d.ts +3 -3
  27. package/dist/node_sampler.js +75 -75
  28. package/dist/opcua_server.d.ts +747 -747
  29. package/dist/opcua_server.js +2431 -2431
  30. package/dist/queue.d.ts +11 -11
  31. package/dist/queue.js +71 -71
  32. package/dist/register_server_manager.d.ts +96 -96
  33. package/dist/register_server_manager.js +584 -584
  34. package/dist/register_server_manager_hidden.d.ts +17 -17
  35. package/dist/register_server_manager_hidden.js +27 -27
  36. package/dist/register_server_manager_mdns_only.d.ts +22 -22
  37. package/dist/register_server_manager_mdns_only.js +55 -55
  38. package/dist/server_capabilities.d.ts +148 -148
  39. package/dist/server_capabilities.js +92 -92
  40. package/dist/server_end_point.d.ts +183 -183
  41. package/dist/server_end_point.js +819 -817
  42. package/dist/server_end_point.js.map +1 -1
  43. package/dist/server_engine.d.ts +317 -317
  44. package/dist/server_engine.js +1716 -1716
  45. package/dist/server_publish_engine.d.ts +113 -113
  46. package/dist/server_publish_engine.js +541 -541
  47. package/dist/server_publish_engine_for_orphan_subscriptions.d.ts +16 -16
  48. package/dist/server_publish_engine_for_orphan_subscriptions.js +51 -51
  49. package/dist/server_session.d.ts +182 -182
  50. package/dist/server_session.js +739 -739
  51. package/dist/server_subscription.d.ts +421 -421
  52. package/dist/server_subscription.js +1346 -1346
  53. package/dist/sessions_compatible_for_transfer.d.ts +2 -2
  54. package/dist/sessions_compatible_for_transfer.js +39 -39
  55. package/dist/user_manager.d.ts +32 -32
  56. package/dist/user_manager.js +74 -74
  57. package/dist/user_manager_ua.d.ts +3 -3
  58. package/dist/user_manager_ua.js +39 -39
  59. package/dist/validate_filter.d.ts +5 -5
  60. package/dist/validate_filter.js +60 -60
  61. package/package.json +48 -47
  62. package/source/factory.ts +2 -2
  63. package/source/server_end_point.ts +1 -0
@@ -1,1347 +1,1347 @@
1
- "use strict";
2
- /**
3
- * @module node-opcua-server
4
- */
5
- // tslint:disable:no-console
6
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
7
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
8
- return new (P || (P = Promise))(function (resolve, reject) {
9
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
10
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
11
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
12
- step((generator = generator.apply(thisArg, _arguments || [])).next());
13
- });
14
- };
15
- Object.defineProperty(exports, "__esModule", { value: true });
16
- exports.Subscription = exports.SubscriptionState = void 0;
17
- const events_1 = require("events");
18
- const chalk = require("chalk");
19
- const node_opcua_address_space_1 = require("node-opcua-address-space");
20
- const node_opcua_assert_1 = require("node-opcua-assert");
21
- const node_opcua_common_1 = require("node-opcua-common");
22
- const node_opcua_data_model_1 = require("node-opcua-data-model");
23
- const node_opcua_debug_1 = require("node-opcua-debug");
24
- const node_opcua_nodeid_1 = require("node-opcua-nodeid");
25
- const node_opcua_object_registry_1 = require("node-opcua-object-registry");
26
- const node_opcua_secure_channel_1 = require("node-opcua-secure-channel");
27
- const node_opcua_service_filter_1 = require("node-opcua-service-filter");
28
- const node_opcua_service_subscription_1 = require("node-opcua-service-subscription");
29
- const node_opcua_service_subscription_2 = require("node-opcua-service-subscription");
30
- const node_opcua_status_code_1 = require("node-opcua-status-code");
31
- const node_opcua_types_1 = require("node-opcua-types");
32
- const queue_1 = require("./queue");
33
- const monitored_item_1 = require("./monitored_item");
34
- const validate_filter_1 = require("./validate_filter");
35
- const i_server_side_publish_engine_1 = require("./i_server_side_publish_engine");
36
- const debugLog = (0, node_opcua_debug_1.make_debugLog)(__filename);
37
- const doDebug = (0, node_opcua_debug_1.checkDebugFlag)(__filename);
38
- const warningLog = (0, node_opcua_debug_1.make_warningLog)(__filename);
39
- const maxNotificationMessagesInQueue = 100;
40
- var SubscriptionState;
41
- (function (SubscriptionState) {
42
- SubscriptionState[SubscriptionState["CLOSED"] = 1] = "CLOSED";
43
- SubscriptionState[SubscriptionState["CREATING"] = 2] = "CREATING";
44
- SubscriptionState[SubscriptionState["NORMAL"] = 3] = "NORMAL";
45
- // The keep-alive counter is not used in this state.
46
- SubscriptionState[SubscriptionState["LATE"] = 4] = "LATE";
47
- // ready to be sent, but there are no Publish requests queued. When in this state, the next Publish
48
- // request is processed when it is received. The keep-alive counter is not used in this state.
49
- SubscriptionState[SubscriptionState["KEEPALIVE"] = 5] = "KEEPALIVE";
50
- // alive counter to count down to 0 from its maximum.
51
- SubscriptionState[SubscriptionState["TERMINATED"] = 6] = "TERMINATED";
52
- })(SubscriptionState = exports.SubscriptionState || (exports.SubscriptionState = {}));
53
- function _adjust_publishing_interval(publishingInterval) {
54
- publishingInterval =
55
- publishingInterval === undefined || Number.isNaN(publishingInterval)
56
- ? Subscription.defaultPublishingInterval
57
- : publishingInterval;
58
- publishingInterval = Math.max(publishingInterval, Subscription.minimumPublishingInterval);
59
- publishingInterval = Math.min(publishingInterval, Subscription.maximumPublishingInterval);
60
- return publishingInterval;
61
- }
62
- const minimumMaxKeepAliveCount = 2;
63
- const maximumMaxKeepAliveCount = 12000;
64
- function _adjust_maxKeepAliveCount(maxKeepAliveCount /*,publishingInterval*/) {
65
- maxKeepAliveCount = maxKeepAliveCount || minimumMaxKeepAliveCount;
66
- maxKeepAliveCount = Math.max(maxKeepAliveCount, minimumMaxKeepAliveCount);
67
- maxKeepAliveCount = Math.min(maxKeepAliveCount, maximumMaxKeepAliveCount);
68
- return maxKeepAliveCount;
69
- }
70
- function _adjust_lifeTimeCount(lifeTimeCount, maxKeepAliveCount, publishingInterval) {
71
- lifeTimeCount = lifeTimeCount || 1;
72
- // let's make sure that lifeTimeCount is at least three time maxKeepAliveCount
73
- // Note : the specs say ( part 3 - CreateSubscriptionParameter )
74
- // "The lifetime count shall be a minimum of three times the keep keep-alive count."
75
- lifeTimeCount = Math.max(lifeTimeCount, maxKeepAliveCount * 3);
76
- const minTicks = Math.ceil(Subscription.minimumLifetimeDuration / publishingInterval);
77
- const maxTicks = Math.floor(Subscription.maximumLifetimeDuration / publishingInterval);
78
- lifeTimeCount = Math.max(minTicks, lifeTimeCount);
79
- lifeTimeCount = Math.min(maxTicks, lifeTimeCount);
80
- return lifeTimeCount;
81
- }
82
- function _adjust_publishingEnable(publishingEnabled) {
83
- return publishingEnabled === null || publishingEnabled === undefined ? true : !!publishingEnabled;
84
- }
85
- function _adjust_maxNotificationsPerPublish(maxNotificationsPerPublish) {
86
- (0, node_opcua_assert_1.assert)(Subscription.maxNotificationPerPublishHighLimit > 0, "Subscription.maxNotificationPerPublishHighLimit must be positive");
87
- maxNotificationsPerPublish = maxNotificationsPerPublish || 0;
88
- (0, node_opcua_assert_1.assert)(typeof maxNotificationsPerPublish === "number");
89
- // must be strictly positive
90
- maxNotificationsPerPublish = maxNotificationsPerPublish >= 0 ? maxNotificationsPerPublish : 0;
91
- if (maxNotificationsPerPublish === 0) {
92
- // if zero then => use our HighLimit
93
- maxNotificationsPerPublish = Subscription.maxNotificationPerPublishHighLimit;
94
- }
95
- else {
96
- // if not zero then should be capped by maxNotificationPerPublishHighLimit
97
- maxNotificationsPerPublish = Math.min(Subscription.maxNotificationPerPublishHighLimit, maxNotificationsPerPublish);
98
- }
99
- (0, node_opcua_assert_1.assert)(maxNotificationsPerPublish !== 0 && maxNotificationsPerPublish <= Subscription.maxNotificationPerPublishHighLimit);
100
- return maxNotificationsPerPublish;
101
- }
102
- function w(s, length) {
103
- return ("000" + s).padStart(length);
104
- }
105
- function t(d) {
106
- return w(d.getHours(), 2) + ":" + w(d.getMinutes(), 2) + ":" + w(d.getSeconds(), 2) + ":" + w(d.getMilliseconds(), 3);
107
- }
108
- function _getSequenceNumbers(arr) {
109
- return arr.map((notificationMessage) => notificationMessage.sequenceNumber);
110
- }
111
- function analyseEventFilterResult(node, eventFilter) {
112
- /* istanbul ignore next */
113
- if (!(eventFilter instanceof node_opcua_service_filter_1.EventFilter)) {
114
- throw new Error("Internal Error");
115
- }
116
- const selectClauseResults = (0, node_opcua_service_filter_1.checkSelectClauses)(node, eventFilter.selectClauses || []);
117
- const whereClauseResult = new node_opcua_types_1.ContentFilterResult();
118
- return new node_opcua_types_1.EventFilterResult({
119
- selectClauseDiagnosticInfos: [],
120
- selectClauseResults,
121
- whereClauseResult
122
- });
123
- }
124
- function analyseDataChangeFilterResult(node, dataChangeFilter) {
125
- (0, node_opcua_assert_1.assert)(dataChangeFilter instanceof node_opcua_service_subscription_2.DataChangeFilter);
126
- // the opcua specification doesn't provide dataChangeFilterResult
127
- return null;
128
- }
129
- function analyseAggregateFilterResult(node, aggregateFilter) {
130
- (0, node_opcua_assert_1.assert)(aggregateFilter instanceof node_opcua_service_subscription_1.AggregateFilter);
131
- return new node_opcua_types_1.AggregateFilterResult({});
132
- }
133
- function _process_filter(node, filter) {
134
- if (!filter) {
135
- return null;
136
- }
137
- if (filter instanceof node_opcua_service_filter_1.EventFilter) {
138
- return analyseEventFilterResult(node, filter);
139
- }
140
- else if (filter instanceof node_opcua_service_subscription_2.DataChangeFilter) {
141
- return analyseDataChangeFilterResult(node, filter);
142
- }
143
- else if (filter instanceof node_opcua_service_subscription_1.AggregateFilter) {
144
- return analyseAggregateFilterResult(node, filter);
145
- }
146
- // istanbul ignore next
147
- throw new Error("invalid filter");
148
- }
149
- /**
150
- * @private
151
- */
152
- function createSubscriptionDiagnostics(subscription) {
153
- (0, node_opcua_assert_1.assert)(subscription instanceof Subscription);
154
- const subscriptionDiagnostics = new node_opcua_common_1.SubscriptionDiagnosticsDataType({});
155
- const subscription_subscriptionDiagnostics = subscriptionDiagnostics;
156
- subscription_subscriptionDiagnostics.$subscription = subscription;
157
- // "sessionId"
158
- subscription_subscriptionDiagnostics.__defineGetter__("sessionId", function () {
159
- if (!this.$subscription) {
160
- return node_opcua_nodeid_1.NodeId.nullNodeId;
161
- }
162
- return this.$subscription.getSessionId();
163
- });
164
- subscription_subscriptionDiagnostics.__defineGetter__("subscriptionId", function () {
165
- if (!this.$subscription) {
166
- return 0;
167
- }
168
- return this.$subscription.id;
169
- });
170
- subscription_subscriptionDiagnostics.__defineGetter__("priority", function () {
171
- if (!this.$subscription) {
172
- return 0;
173
- }
174
- return this.$subscription.priority;
175
- });
176
- subscription_subscriptionDiagnostics.__defineGetter__("publishingInterval", function () {
177
- if (!this.$subscription) {
178
- return 0;
179
- }
180
- return this.$subscription.publishingInterval;
181
- });
182
- subscription_subscriptionDiagnostics.__defineGetter__("maxLifetimeCount", function () {
183
- return this.$subscription.lifeTimeCount;
184
- });
185
- subscription_subscriptionDiagnostics.__defineGetter__("maxKeepAliveCount", function () {
186
- if (!this.$subscription) {
187
- return 0;
188
- }
189
- return this.$subscription.maxKeepAliveCount;
190
- });
191
- subscription_subscriptionDiagnostics.__defineGetter__("maxNotificationsPerPublish", function () {
192
- if (!this.$subscription) {
193
- return 0;
194
- }
195
- return this.$subscription.maxNotificationsPerPublish;
196
- });
197
- subscription_subscriptionDiagnostics.__defineGetter__("publishingEnabled", function () {
198
- if (!this.$subscription) {
199
- return false;
200
- }
201
- return this.$subscription.publishingEnabled;
202
- });
203
- subscription_subscriptionDiagnostics.__defineGetter__("monitoredItemCount", function () {
204
- if (!this.$subscription) {
205
- return 0;
206
- }
207
- return this.$subscription.monitoredItemCount;
208
- });
209
- subscription_subscriptionDiagnostics.__defineGetter__("nextSequenceNumber", function () {
210
- if (!this.$subscription) {
211
- return 0;
212
- }
213
- return this.$subscription._get_future_sequence_number();
214
- });
215
- subscription_subscriptionDiagnostics.__defineGetter__("disabledMonitoredItemCount", function () {
216
- if (!this.$subscription) {
217
- return 0;
218
- }
219
- return this.$subscription.disabledMonitoredItemCount;
220
- });
221
- /* those member of self.subscriptionDiagnostics are handled directly
222
-
223
- modifyCount
224
- enableCount,
225
- disableCount,
226
- republishRequestCount,
227
- notificationsCount,
228
- publishRequestCount,
229
- dataChangeNotificationsCount,
230
- eventNotificationsCount,
231
- */
232
- /*
233
- those members are not updated yet in the code :
234
- "republishMessageRequestCount",
235
- "republishMessageCount",
236
- "transferRequestCount",
237
- "transferredToAltClientCount",
238
- "transferredToSameClientCount",
239
- "latePublishRequestCount",
240
- "unacknowledgedMessageCount",
241
- "discardedMessageCount",
242
- "monitoringQueueOverflowCount",
243
- "eventQueueOverFlowCount"
244
- */
245
- subscription_subscriptionDiagnostics.__defineGetter__("currentKeepAliveCount", function () {
246
- if (!this.$subscription) {
247
- return 0;
248
- }
249
- return this.$subscription.currentKeepAliveCount;
250
- });
251
- subscription_subscriptionDiagnostics.__defineGetter__("currentLifetimeCount", function () {
252
- if (!this.$subscription) {
253
- return 0;
254
- }
255
- return this.$subscription.currentLifetimeCount;
256
- });
257
- // add object in Variable SubscriptionDiagnosticArray (i=2290) ( Array of SubscriptionDiagnostics)
258
- // add properties in Variable to reflect
259
- return subscriptionDiagnostics;
260
- }
261
- let g_monitoredItemId = Math.ceil(Math.random() * 100000);
262
- function getNextMonitoredItemId() {
263
- return g_monitoredItemId++;
264
- }
265
- // function myFilter<T>(t1: any, chunk: any[]): T[] {
266
- // return chunk.filter(filter_instanceof.bind(null, t1));
267
- // }
268
- // function makeNotificationData(notifications_chunk: QueueItem): NotificationData {
269
- // const dataChangedNotificationData = myFilter<MonitoredItemNotification>(MonitoredItemNotification, notifications_chunk);
270
- // const eventNotificationListData = myFilter<EventFieldList>(EventFieldList, notifications_chunk);
271
- // assert(notifications_chunk.length === dataChangedNotificationData.length + eventNotificationListData.length);
272
- // const notifications: (DataChangeNotification | EventNotificationList)[] = [];
273
- // // add dataChangeNotification
274
- // if (dataChangedNotificationData.length) {
275
- // const dataChangeNotification = new DataChangeNotification({
276
- // diagnosticInfos: [],
277
- // monitoredItems: dataChangedNotificationData
278
- // });
279
- // notifications.push(dataChangeNotification);
280
- // }
281
- // // add dataChangeNotification
282
- // if (eventNotificationListData.length) {
283
- // const eventNotificationList = new EventNotificationList({
284
- // events: eventNotificationListData
285
- // });
286
- // notifications.push(eventNotificationList);
287
- // }
288
- // return notifications.length === 0 ? null : notifications;
289
- // }
290
- const INVALID_ID = -1;
291
- /**
292
- * The Subscription class used in the OPCUA server side.
293
- */
294
- class Subscription extends events_1.EventEmitter {
295
- constructor(options) {
296
- super();
297
- this._state = -1;
298
- this._keep_alive_counter = 0;
299
- this._hasUncollectedMonitoredItemNotifications = false;
300
- options = options || {};
301
- Subscription.registry.register(this);
302
- (0, node_opcua_assert_1.assert)(this.sessionId instanceof node_opcua_nodeid_1.NodeId, "expecting a sessionId NodeId");
303
- this.publishEngine = options.publishEngine;
304
- this.id = options.id || INVALID_ID;
305
- this.priority = options.priority || 0;
306
- this.publishingInterval = _adjust_publishing_interval(options.publishingInterval);
307
- this.maxKeepAliveCount = _adjust_maxKeepAliveCount(options.maxKeepAliveCount); // , this.publishingInterval);
308
- this.resetKeepAliveCounter();
309
- this.lifeTimeCount = _adjust_lifeTimeCount(options.lifeTimeCount || 0, this.maxKeepAliveCount, this.publishingInterval);
310
- this.maxNotificationsPerPublish = _adjust_maxNotificationsPerPublish(options.maxNotificationsPerPublish);
311
- this._life_time_counter = 0;
312
- this.resetLifeTimeCounter();
313
- // notification message that are ready to be sent to the client
314
- this._pending_notifications = new queue_1.Queue();
315
- this._sent_notification_messages = [];
316
- this._sequence_number_generator = new node_opcua_secure_channel_1.SequenceNumberGenerator();
317
- // initial state of the subscription
318
- this.state = SubscriptionState.CREATING;
319
- this.publishIntervalCount = 0;
320
- this.monitoredItems = {}; // monitored item map
321
- this.monitoredItemIdCounter = 0;
322
- this.publishingEnabled = _adjust_publishingEnable(options.publishingEnabled);
323
- this.subscriptionDiagnostics = createSubscriptionDiagnostics(this);
324
- // A boolean value that is set to TRUE to mean that either a NotificationMessage or a keep-alive
325
- // Message has been sent on the Subscription. It is a flag that is used to ensure that either a
326
- // NotificationMessage or a keep-alive Message is sent out the first time the publishing
327
- // timer expires.
328
- this.messageSent = false;
329
- this.timerId = null;
330
- this._start_timer({ firstTime: true });
331
- debugLog(chalk.green(`creating subscription ${this.id}`));
332
- this.serverCapabilities = options.serverCapabilities;
333
- this.serverCapabilities.maxMonitoredItems =
334
- this.serverCapabilities.maxMonitoredItems || Subscription.defaultMaxMonitoredItemCount;
335
- this.serverCapabilities.maxMonitoredItemsPerSubscription =
336
- this.serverCapabilities.maxMonitoredItemsPerSubscription || Subscription.defaultMaxMonitoredItemCount;
337
- this.globalCounter = options.globalCounter;
338
- }
339
- /**
340
- * @deprecated use serverCapacity.maxMonitoredItems and serverCapacity.maxMonitoredItemsPerSubscription instead
341
- */
342
- static get maxMonitoredItemCount() {
343
- return Subscription.defaultMaxMonitoredItemCount;
344
- }
345
- set state(value) {
346
- if (this._state !== value) {
347
- this._state = value;
348
- this.emit("stateChanged", value);
349
- }
350
- }
351
- get state() {
352
- return this._state;
353
- }
354
- get sessionId() {
355
- return this.$session ? this.$session.nodeId : node_opcua_nodeid_1.NodeId.nullNodeId;
356
- }
357
- get currentLifetimeCount() {
358
- return this._life_time_counter;
359
- }
360
- get currentKeepAliveCount() {
361
- return this._keep_alive_counter;
362
- }
363
- getSessionId() {
364
- return this.sessionId;
365
- }
366
- toString() {
367
- let str = "Subscription:\n";
368
- str += " subscriptionId " + this.id + "\n";
369
- str += " sessionId " + this.getSessionId().toString() + "\n";
370
- str += " publishingEnabled " + this.publishingEnabled + "\n";
371
- str += " maxKeepAliveCount " + this.maxKeepAliveCount + "\n";
372
- str += " publishingInterval " + this.publishingInterval + "\n";
373
- str += " lifeTimeCount " + this.lifeTimeCount + "\n";
374
- str += " maxKeepAliveCount " + this.maxKeepAliveCount + "\n";
375
- return str;
376
- }
377
- /**
378
- * modify subscription parameters
379
- * @param param
380
- */
381
- modify(param) {
382
- // update diagnostic counter
383
- this.subscriptionDiagnostics.modifyCount += 1;
384
- const publishingInterval_old = this.publishingInterval;
385
- param.requestedPublishingInterval = param.requestedPublishingInterval || 0;
386
- param.requestedMaxKeepAliveCount = param.requestedMaxKeepAliveCount || this.maxKeepAliveCount;
387
- param.requestedLifetimeCount = param.requestedLifetimeCount || this.lifeTimeCount;
388
- this.publishingInterval = _adjust_publishing_interval(param.requestedPublishingInterval);
389
- this.maxKeepAliveCount = _adjust_maxKeepAliveCount(param.requestedMaxKeepAliveCount);
390
- this.lifeTimeCount = _adjust_lifeTimeCount(param.requestedLifetimeCount, this.maxKeepAliveCount, this.publishingInterval);
391
- this.maxNotificationsPerPublish = _adjust_maxNotificationsPerPublish(param.maxNotificationsPerPublish || 0);
392
- this.priority = param.priority || 0;
393
- this.resetLifeTimeAndKeepAliveCounters();
394
- if (publishingInterval_old !== this.publishingInterval) {
395
- // todo
396
- }
397
- this._stop_timer();
398
- this._start_timer({ firstTime: false });
399
- }
400
- /**
401
- * set publishing mode
402
- * @param publishingEnabled
403
- */
404
- setPublishingMode(publishingEnabled) {
405
- this.publishingEnabled = !!publishingEnabled;
406
- // update diagnostics
407
- if (this.publishingEnabled) {
408
- this.subscriptionDiagnostics.enableCount += 1;
409
- }
410
- else {
411
- this.subscriptionDiagnostics.disableCount += 1;
412
- }
413
- this.resetLifeTimeCounter();
414
- if (!publishingEnabled && this.state !== SubscriptionState.CLOSED) {
415
- this.state = SubscriptionState.NORMAL;
416
- }
417
- return node_opcua_status_code_1.StatusCodes.Good;
418
- }
419
- /**
420
- * @private
421
- */
422
- get keepAliveCounterHasExpired() {
423
- return this._keep_alive_counter >= this.maxKeepAliveCount || this.state === SubscriptionState.LATE;
424
- }
425
- /**
426
- * Reset the Lifetime Counter Variable to the value specified for the lifetime of a Subscription in
427
- * the CreateSubscription Service( 5.13.2).
428
- * @private
429
- */
430
- resetLifeTimeCounter() {
431
- this._life_time_counter = 0;
432
- }
433
- /**
434
- * @private
435
- */
436
- increaseLifeTimeCounter() {
437
- this._life_time_counter += 1;
438
- if (this._life_time_counter >= this.lifeTimeCount) {
439
- this.emit("lifeTimeExpired");
440
- }
441
- this.emit("lifeTimeCounterChanged", this._life_time_counter);
442
- }
443
- /**
444
- * True if the subscription life time has expired.
445
- *
446
- */
447
- get lifeTimeHasExpired() {
448
- (0, node_opcua_assert_1.assert)(this.lifeTimeCount > 0);
449
- return this._life_time_counter >= this.lifeTimeCount;
450
- }
451
- /**
452
- * number of milliseconds before this subscription times out (lifeTimeHasExpired === true);
453
- */
454
- get timeToExpiration() {
455
- return (this.lifeTimeCount - this._life_time_counter) * this.publishingInterval;
456
- }
457
- get timeToKeepAlive() {
458
- return (this.maxKeepAliveCount - this._keep_alive_counter) * this.publishingInterval;
459
- }
460
- /**
461
- * Terminates the subscription.
462
- * Calling this method will also remove any monitored items.
463
- *
464
- */
465
- terminate() {
466
- (0, node_opcua_assert_1.assert)(arguments.length === 0);
467
- debugLog("Subscription#terminate status", SubscriptionState[this.state]);
468
- if (this.state === SubscriptionState.CLOSED) {
469
- // todo verify if asserting is required here
470
- return;
471
- }
472
- // stop timer
473
- this._stop_timer();
474
- debugLog("terminating Subscription ", this.id, " with ", this.monitoredItemCount, " monitored items");
475
- // dispose all monitoredItem
476
- const keys = Object.keys(this.monitoredItems);
477
- for (const key of keys) {
478
- const status = this.removeMonitoredItem(parseInt(key, 10));
479
- (0, node_opcua_assert_1.assert)(status === node_opcua_status_code_1.StatusCodes.Good);
480
- }
481
- (0, node_opcua_assert_1.assert)(this.monitoredItemCount === 0);
482
- if (this.$session) {
483
- this.$session._unexposeSubscriptionDiagnostics(this);
484
- }
485
- this.state = SubscriptionState.CLOSED;
486
- /**
487
- * notify the subscription owner that the subscription has been terminated.
488
- * @event "terminated"
489
- */
490
- this.emit("terminated");
491
- if (this.publishEngine) {
492
- this.publishEngine.on_close_subscription(this);
493
- }
494
- }
495
- setTriggering(triggeringItemId, linksToAdd, linksToRemove) {
496
- /** Bad_NothingToDo, Bad_TooManyOperations,Bad_SubscriptionIdInvalid, Bad_MonitoredItemIdInvalid */
497
- linksToAdd = linksToAdd || [];
498
- linksToRemove = linksToRemove || [];
499
- if (linksToAdd.length === 0 && linksToRemove.length === 0) {
500
- return { statusCode: node_opcua_status_code_1.StatusCodes.BadNothingToDo, addResults: [], removeResults: [] };
501
- }
502
- const triggeringItem = this.getMonitoredItem(triggeringItemId);
503
- const monitoredItemsToAdd = linksToAdd.map((id) => this.getMonitoredItem(id));
504
- const monitoredItemsToRemove = linksToRemove.map((id) => this.getMonitoredItem(id));
505
- if (!triggeringItem) {
506
- const removeResults1 = monitoredItemsToRemove.map((m) => m ? node_opcua_status_code_1.StatusCodes.Good : node_opcua_status_code_1.StatusCodes.BadMonitoredItemIdInvalid);
507
- const addResults1 = monitoredItemsToAdd.map((m) => m ? node_opcua_status_code_1.StatusCodes.Good : node_opcua_status_code_1.StatusCodes.BadMonitoredItemIdInvalid);
508
- return {
509
- statusCode: node_opcua_status_code_1.StatusCodes.BadMonitoredItemIdInvalid,
510
- addResults: addResults1,
511
- removeResults: removeResults1
512
- };
513
- }
514
- //
515
- // note: it seems that CTT imposed that we do remove before add
516
- const removeResults = monitoredItemsToRemove.map((m) => !m ? node_opcua_status_code_1.StatusCodes.BadMonitoredItemIdInvalid : triggeringItem.removeLinkItem(m.monitoredItemId));
517
- const addResults = monitoredItemsToAdd.map((m) => !m ? node_opcua_status_code_1.StatusCodes.BadMonitoredItemIdInvalid : triggeringItem.addLinkItem(m.monitoredItemId));
518
- const statusCode = node_opcua_status_code_1.StatusCodes.Good;
519
- // do binding
520
- return {
521
- statusCode,
522
- addResults,
523
- removeResults
524
- };
525
- }
526
- dispose() {
527
- if (doDebug) {
528
- debugLog("Subscription#dispose", this.id, this.monitoredItemCount);
529
- }
530
- (0, node_opcua_assert_1.assert)(this.monitoredItemCount === 0, "MonitoredItems haven't been deleted first !!!");
531
- (0, node_opcua_assert_1.assert)(this.timerId === null, "Subscription timer haven't been terminated");
532
- if (this.subscriptionDiagnostics) {
533
- this.subscriptionDiagnostics.$subscription = null;
534
- }
535
- this.publishEngine = undefined;
536
- this._pending_notifications.clear();
537
- this._sent_notification_messages = [];
538
- this.$session = undefined;
539
- this.removeAllListeners();
540
- Subscription.registry.unregister(this);
541
- }
542
- get aborted() {
543
- const session = this.$session;
544
- if (!session) {
545
- return true;
546
- }
547
- return session.aborted;
548
- }
549
- /**
550
- * number of pending notifications
551
- */
552
- get pendingNotificationsCount() {
553
- return this._pending_notifications ? this._pending_notifications.size : 0;
554
- }
555
- /**
556
- * is 'true' if there are pending notifications for this subscription. (i.e moreNotifications)
557
- */
558
- get hasPendingNotifications() {
559
- return this.pendingNotificationsCount > 0;
560
- }
561
- /**
562
- * number of sent notifications
563
- */
564
- get sentNotificationMessageCount() {
565
- return this._sent_notification_messages.length;
566
- }
567
- /**
568
- * @internal
569
- */
570
- _flushSentNotifications() {
571
- const tmp = this._sent_notification_messages;
572
- this._sent_notification_messages = [];
573
- return tmp;
574
- }
575
- /**
576
- * number of monitored items handled by this subscription
577
- */
578
- get monitoredItemCount() {
579
- return Object.keys(this.monitoredItems).length;
580
- }
581
- /**
582
- * number of disabled monitored items.
583
- */
584
- get disabledMonitoredItemCount() {
585
- return Object.values(this.monitoredItems).reduce((cumul, monitoredItem) => {
586
- return cumul + (monitoredItem.monitoringMode === node_opcua_service_subscription_2.MonitoringMode.Disabled ? 1 : 0);
587
- }, 0);
588
- }
589
- /**
590
- * The number of unacknowledged messages saved in the republish queue.
591
- */
592
- get unacknowledgedMessageCount() {
593
- return this.subscriptionDiagnostics.unacknowledgedMessageCount;
594
- }
595
- /**
596
- * adjust monitored item sampling interval
597
- * - an samplingInterval ===0 means that we use a event-base model ( no sampling)
598
- * - otherwise the sampling is adjusted
599
- * @private
600
- */
601
- adjustSamplingInterval(samplingInterval, node) {
602
- if (samplingInterval < 0) {
603
- // - The value -1 indicates that the default sampling interval defined by the publishing
604
- // interval of the Subscription is requested.
605
- // - Any negative number is interpreted as -1.
606
- samplingInterval = this.publishingInterval;
607
- }
608
- else if (samplingInterval === 0) {
609
- // OPCUA 1.0.3 Part 4 - 5.12.1.2
610
- // The value 0 indicates that the Server should use the fastest practical rate.
611
- // The fastest supported sampling interval may be equal to 0, which indicates
612
- // that the data item is exception-based rather than being sampled at some period.
613
- // An exception-based model means that the underlying system does not require
614
- // sampling and reports data changes.
615
- const dataValueSamplingInterval = node.readAttribute(node_opcua_address_space_1.SessionContext.defaultContext, node_opcua_data_model_1.AttributeIds.MinimumSamplingInterval);
616
- // TODO if attributeId === AttributeIds.Value : sampling interval required here
617
- if (dataValueSamplingInterval.statusCode === node_opcua_status_code_1.StatusCodes.Good) {
618
- // node provides a Minimum sampling interval ...
619
- samplingInterval = dataValueSamplingInterval.value.value;
620
- (0, node_opcua_assert_1.assert)(samplingInterval >= 0 && samplingInterval <= monitored_item_1.MonitoredItem.maximumSamplingInterval);
621
- // note : at this stage, a samplingInterval===0 means that the data item is really exception-based
622
- }
623
- }
624
- else if (samplingInterval < monitored_item_1.MonitoredItem.minimumSamplingInterval) {
625
- samplingInterval = monitored_item_1.MonitoredItem.minimumSamplingInterval;
626
- }
627
- else if (samplingInterval > monitored_item_1.MonitoredItem.maximumSamplingInterval) {
628
- // If the requested samplingInterval is higher than the
629
- // maximum sampling interval supported by the Server, the maximum sampling
630
- // interval is returned.
631
- samplingInterval = monitored_item_1.MonitoredItem.maximumSamplingInterval;
632
- }
633
- const node_minimumSamplingInterval = node && node.minimumSamplingInterval ? node.minimumSamplingInterval : 0;
634
- samplingInterval = Math.max(samplingInterval, node_minimumSamplingInterval);
635
- return samplingInterval;
636
- }
637
- /**
638
- * create a monitored item
639
- * @param addressSpace - address space
640
- * @param timestampsToReturn - the timestamp to return
641
- * @param monitoredItemCreateRequest - the parameters describing the monitored Item to create
642
- */
643
- preCreateMonitoredItem(addressSpace, timestampsToReturn, monitoredItemCreateRequest) {
644
- (0, node_opcua_assert_1.assert)(monitoredItemCreateRequest instanceof node_opcua_service_subscription_2.MonitoredItemCreateRequest);
645
- function handle_error(statusCode) {
646
- return {
647
- createResult: new node_opcua_service_subscription_2.MonitoredItemCreateResult({ statusCode }),
648
- monitoredItemCreateRequest
649
- };
650
- }
651
- const itemToMonitor = monitoredItemCreateRequest.itemToMonitor;
652
- const node = addressSpace.findNode(itemToMonitor.nodeId);
653
- if (!node) {
654
- return handle_error(node_opcua_status_code_1.StatusCodes.BadNodeIdUnknown);
655
- }
656
- if (itemToMonitor.attributeId === node_opcua_data_model_1.AttributeIds.Value && !(node.nodeClass === node_opcua_data_model_1.NodeClass.Variable)) {
657
- // AttributeIds.Value is only valid for monitoring value of UAVariables.
658
- return handle_error(node_opcua_status_code_1.StatusCodes.BadAttributeIdInvalid);
659
- }
660
- if (itemToMonitor.attributeId === node_opcua_data_model_1.AttributeIds.INVALID) {
661
- return handle_error(node_opcua_status_code_1.StatusCodes.BadAttributeIdInvalid);
662
- }
663
- if (!itemToMonitor.indexRange.isValid()) {
664
- return handle_error(node_opcua_status_code_1.StatusCodes.BadIndexRangeInvalid);
665
- }
666
- // check dataEncoding applies only on Values
667
- if (itemToMonitor.dataEncoding.name && itemToMonitor.attributeId !== node_opcua_data_model_1.AttributeIds.Value) {
668
- return handle_error(node_opcua_status_code_1.StatusCodes.BadDataEncodingInvalid);
669
- }
670
- // check dataEncoding
671
- if (!(0, node_opcua_data_model_1.isValidDataEncoding)(itemToMonitor.dataEncoding)) {
672
- return handle_error(node_opcua_status_code_1.StatusCodes.BadDataEncodingUnsupported);
673
- }
674
- // check that item can be read by current user session
675
- // filter
676
- const requestedParameters = monitoredItemCreateRequest.requestedParameters;
677
- const filter = requestedParameters.filter;
678
- const statusCodeFilter = (0, validate_filter_1.validateFilter)(filter, itemToMonitor, node);
679
- if (statusCodeFilter !== node_opcua_status_code_1.StatusCodes.Good) {
680
- return handle_error(statusCodeFilter);
681
- }
682
- // do we have enough room for new monitored items ?
683
- if (this.monitoredItemCount >= this.serverCapabilities.maxMonitoredItemsPerSubscription) {
684
- return handle_error(node_opcua_status_code_1.StatusCodes.BadTooManyMonitoredItems);
685
- }
686
- if (this.globalCounter.totalMonitoredItemCount >= this.serverCapabilities.maxMonitoredItems) {
687
- return handle_error(node_opcua_status_code_1.StatusCodes.BadTooManyMonitoredItems);
688
- }
689
- const createResult = this._createMonitoredItemStep2(timestampsToReturn, monitoredItemCreateRequest, node);
690
- (0, node_opcua_assert_1.assert)(createResult.statusCode === node_opcua_status_code_1.StatusCodes.Good);
691
- const monitoredItem = this.getMonitoredItem(createResult.monitoredItemId);
692
- // istanbul ignore next
693
- if (!monitoredItem) {
694
- throw new Error("internal error");
695
- }
696
- // TODO: fix old way to set node. !!!!
697
- monitoredItem.setNode(node);
698
- this.emit("monitoredItem", monitoredItem, itemToMonitor);
699
- return { monitoredItem, monitoredItemCreateRequest, createResult };
700
- }
701
- applyOnMonitoredItem(functor) {
702
- return __awaiter(this, void 0, void 0, function* () {
703
- for (const m of Object.values(this.monitoredItems)) {
704
- yield functor(m);
705
- }
706
- });
707
- }
708
- postCreateMonitoredItem(monitoredItem, monitoredItemCreateRequest, createResult) {
709
- this._createMonitoredItemStep3(monitoredItem, monitoredItemCreateRequest);
710
- }
711
- createMonitoredItem(addressSpace, timestampsToReturn, monitoredItemCreateRequest) {
712
- const { monitoredItem, createResult } = this.preCreateMonitoredItem(addressSpace, timestampsToReturn, monitoredItemCreateRequest);
713
- this.postCreateMonitoredItem(monitoredItem, monitoredItemCreateRequest, createResult);
714
- return createResult;
715
- }
716
- /**
717
- * get a monitoredItem by Id.
718
- * @param monitoredItemId : the id of the monitored item to get.
719
- * @return the monitored item matching monitoredItemId
720
- */
721
- getMonitoredItem(monitoredItemId) {
722
- return this.monitoredItems[monitoredItemId] || null;
723
- }
724
- /**
725
- * remove a monitored Item from the subscription.
726
- * @param monitoredItemId : the id of the monitored item to get.
727
- */
728
- removeMonitoredItem(monitoredItemId) {
729
- debugLog("Removing monitoredIem ", monitoredItemId);
730
- if (!Object.prototype.hasOwnProperty.call(this.monitoredItems, monitoredItemId.toString())) {
731
- return node_opcua_status_code_1.StatusCodes.BadMonitoredItemIdInvalid;
732
- }
733
- const monitoredItem = this.monitoredItems[monitoredItemId];
734
- monitoredItem.terminate();
735
- monitoredItem.dispose();
736
- /**
737
- *
738
- * notify that a monitored item has been removed from the subscription
739
- * @param monitoredItem {MonitoredItem}
740
- */
741
- this.emit("removeMonitoredItem", monitoredItem);
742
- delete this.monitoredItems[monitoredItemId];
743
- this.globalCounter.totalMonitoredItemCount -= 1;
744
- this._removePendingNotificationsFor(monitoredItemId);
745
- // flush pending notifications
746
- // assert(this._pending_notifications.size === 0);
747
- return node_opcua_status_code_1.StatusCodes.Good;
748
- }
749
- /**
750
- * rue if monitored Item have uncollected Notifications
751
- */
752
- get hasUncollectedMonitoredItemNotifications() {
753
- if (this._hasUncollectedMonitoredItemNotifications) {
754
- return true;
755
- }
756
- const keys = Object.keys(this.monitoredItems);
757
- const n = keys.length;
758
- for (let i = 0; i < n; i++) {
759
- const key = parseInt(keys[i], 10);
760
- const monitoredItem = this.monitoredItems[key];
761
- if (monitoredItem.hasMonitoredItemNotifications) {
762
- this._hasUncollectedMonitoredItemNotifications = true;
763
- return true;
764
- }
765
- }
766
- return false;
767
- }
768
- get subscriptionId() {
769
- return this.id;
770
- }
771
- getMessageForSequenceNumber(sequenceNumber) {
772
- const notification_message = this._sent_notification_messages.find((e) => e.sequenceNumber === sequenceNumber);
773
- return notification_message || null;
774
- }
775
- /**
776
- * returns true if the notification has expired
777
- * @param notification
778
- */
779
- notificationHasExpired(notification) {
780
- (0, node_opcua_assert_1.assert)(Object.prototype.hasOwnProperty.call(notification, "start_tick"));
781
- (0, node_opcua_assert_1.assert)(isFinite(notification.start_tick + this.maxKeepAliveCount));
782
- return notification.start_tick + this.maxKeepAliveCount < this.publishIntervalCount;
783
- }
784
- /**
785
- * returns in an array the sequence numbers of the notifications that have been sent
786
- * and that haven't been acknowledged yet.
787
- */
788
- getAvailableSequenceNumbers() {
789
- const availableSequenceNumbers = _getSequenceNumbers(this._sent_notification_messages);
790
- return availableSequenceNumbers;
791
- }
792
- /**
793
- * acknowledges a notification identified by its sequence number
794
- */
795
- acknowledgeNotification(sequenceNumber) {
796
- debugLog("acknowledgeNotification ", sequenceNumber);
797
- let foundIndex = -1;
798
- this._sent_notification_messages.forEach((e, index) => {
799
- if (e.sequenceNumber === sequenceNumber) {
800
- foundIndex = index;
801
- }
802
- });
803
- if (foundIndex === -1) {
804
- if (doDebug) {
805
- debugLog(chalk.red("acknowledging sequence FAILED !!! "), chalk.cyan(sequenceNumber.toString()));
806
- }
807
- return node_opcua_status_code_1.StatusCodes.BadSequenceNumberUnknown;
808
- }
809
- else {
810
- if (doDebug) {
811
- debugLog(chalk.yellow("acknowledging sequence "), chalk.cyan(sequenceNumber.toString()));
812
- }
813
- this._sent_notification_messages.splice(foundIndex, 1);
814
- this.subscriptionDiagnostics.unacknowledgedMessageCount--;
815
- return node_opcua_status_code_1.StatusCodes.Good;
816
- }
817
- }
818
- /**
819
- * getMonitoredItems is used to get information about monitored items of a subscription.Its intended
820
- * use is defined in Part 4. This method is the implementation of the Standard OPCUA GetMonitoredItems Method.
821
- * from spec:
822
- * This method can be used to get the list of monitored items in a subscription if CreateMonitoredItems
823
- * failed due to a network interruption and the client does not know if the creation succeeded in the server.
824
- *
825
- */
826
- getMonitoredItems() {
827
- const monitoredItems = Object.keys(this.monitoredItems);
828
- const monitoredItemCount = monitoredItems.length;
829
- const result = {
830
- clientHandles: new Uint32Array(monitoredItemCount),
831
- serverHandles: new Uint32Array(monitoredItemCount),
832
- statusCode: node_opcua_status_code_1.StatusCodes.Good
833
- };
834
- for (let index = 0; index < monitoredItemCount; index++) {
835
- const monitoredItemId = monitoredItems[index];
836
- const serverHandle = parseInt(monitoredItemId, 10);
837
- const monitoredItem = this.getMonitoredItem(serverHandle);
838
- result.clientHandles[index] = monitoredItem.clientHandle;
839
- // TODO: serverHandle is defined anywhere in the OPCUA Specification 1.02
840
- // I am not sure what shall be reported for serverHandle...
841
- // using monitoredItem.monitoredItemId instead...
842
- // May be a clarification in the OPCUA Spec is required.
843
- result.serverHandles[index] = serverHandle;
844
- }
845
- return result;
846
- }
847
- /**
848
- * @private
849
- */
850
- resendInitialValues() {
851
- return __awaiter(this, void 0, void 0, function* () {
852
- const promises = [];
853
- for (const monitoredItem of Object.values(this.monitoredItems)) {
854
- (0, node_opcua_assert_1.assert)(monitoredItem.clientHandle !== 4294967295);
855
- promises.push(monitoredItem.resendInitialValues());
856
- }
857
- yield Promise.all(promises);
858
- this._harvestMonitoredItems();
859
- });
860
- }
861
- /**
862
- * @private
863
- */
864
- notifyTransfer() {
865
- var _a;
866
- // OPCUA UA Spec 1.0.3 : part 3 - page 82 - 5.13.7 TransferSubscriptions:
867
- // If the Server transfers the Subscription to the new Session, the Server shall issue
868
- // a StatusChangeNotification notificationMessage with the status code
869
- // Good_SubscriptionTransferred to the old Session.
870
- debugLog(chalk.red(" Subscription => Notifying Transfer "));
871
- const notificationData = new node_opcua_service_subscription_2.StatusChangeNotification({
872
- status: node_opcua_status_code_1.StatusCodes.GoodSubscriptionTransferred
873
- });
874
- if (this.publishEngine.pendingPublishRequestCount) {
875
- // the GoodSubscriptionTransferred can be processed immediately
876
- this._addNotificationMessage(notificationData);
877
- debugLog(chalk.red("pendingPublishRequestCount"), (_a = this.publishEngine) === null || _a === void 0 ? void 0 : _a.pendingPublishRequestCount);
878
- this._publish_pending_notifications();
879
- }
880
- else {
881
- debugLog(chalk.red("Cannot send GoodSubscriptionTransferred => lets create a TransferredSubscription "));
882
- const ts = new i_server_side_publish_engine_1.TransferredSubscription({
883
- generator: this._sequence_number_generator,
884
- id: this.id,
885
- publishEngine: this.publishEngine
886
- });
887
- ts._pending_notification = notificationData;
888
- this.publishEngine._closed_subscriptions.push(ts);
889
- }
890
- }
891
- /**
892
- *
893
- * the server invokes the resetLifeTimeAndKeepAliveCounters method of the subscription
894
- * when the server has send a Publish Response, so that the subscription
895
- * can reset its life time counter.
896
- *
897
- * @private
898
- */
899
- resetLifeTimeAndKeepAliveCounters() {
900
- this.resetLifeTimeCounter();
901
- this.resetKeepAliveCounter();
902
- }
903
- _updateCounters(notificationMessage) {
904
- for (const notificationData of notificationMessage.notificationData || []) {
905
- // update diagnostics
906
- if (notificationData instanceof node_opcua_service_subscription_2.DataChangeNotification) {
907
- const nbNotifs = notificationData.monitoredItems.length;
908
- this.subscriptionDiagnostics.dataChangeNotificationsCount += nbNotifs;
909
- this.subscriptionDiagnostics.notificationsCount += nbNotifs;
910
- }
911
- else if (notificationData instanceof node_opcua_service_subscription_2.EventNotificationList) {
912
- const nbNotifs = notificationData.events.length;
913
- this.subscriptionDiagnostics.eventNotificationsCount += nbNotifs;
914
- this.subscriptionDiagnostics.notificationsCount += nbNotifs;
915
- }
916
- else {
917
- (0, node_opcua_assert_1.assert)(notificationData instanceof node_opcua_service_subscription_2.StatusChangeNotification);
918
- // TODO
919
- // note: :there is no way to count StatusChangeNotifications in opcua yet.
920
- }
921
- }
922
- }
923
- /**
924
- * _publish_pending_notifications send a "notification" event:
925
- *
926
- * @private
927
- * @precondition
928
- * - pendingPublishRequestCount > 0
929
- */
930
- _publish_pending_notifications() {
931
- var _a;
932
- const publishEngine = this.publishEngine;
933
- const subscriptionId = this.id;
934
- // preconditions
935
- (0, node_opcua_assert_1.assert)(publishEngine.pendingPublishRequestCount > 0);
936
- (0, node_opcua_assert_1.assert)(this.hasPendingNotifications);
937
- const notificationMessage = this._popNotificationToSend();
938
- if (notificationMessage.notificationData.length === 0) {
939
- return; // nothing to do
940
- }
941
- const moreNotifications = this.hasPendingNotifications;
942
- this.emit("notification", notificationMessage);
943
- // Update counters ....
944
- this._updateCounters(notificationMessage);
945
- (0, node_opcua_assert_1.assert)(Object.prototype.hasOwnProperty.call(notificationMessage, "sequenceNumber"));
946
- (0, node_opcua_assert_1.assert)(Object.prototype.hasOwnProperty.call(notificationMessage, "notificationData"));
947
- // update diagnostics
948
- this.subscriptionDiagnostics.publishRequestCount += 1;
949
- const response = new node_opcua_service_subscription_2.PublishResponse({
950
- moreNotifications,
951
- notificationMessage: {
952
- notificationData: notificationMessage.notificationData,
953
- sequenceNumber: this._get_next_sequence_number()
954
- },
955
- subscriptionId
956
- });
957
- this._sent_notification_messages.push(response.notificationMessage);
958
- // get available sequence number;
959
- const availableSequenceNumbers = this.getAvailableSequenceNumbers();
960
- (0, node_opcua_assert_1.assert)(!response.notificationMessage ||
961
- availableSequenceNumbers[availableSequenceNumbers.length - 1] === response.notificationMessage.sequenceNumber);
962
- response.availableSequenceNumbers = availableSequenceNumbers;
963
- publishEngine._send_response(this, response);
964
- this.messageSent = true;
965
- this.subscriptionDiagnostics.unacknowledgedMessageCount++;
966
- this.resetLifeTimeAndKeepAliveCounters();
967
- if (doDebug) {
968
- debugLog("Subscription sending a notificationMessage subscriptionId=", subscriptionId, "sequenceNumber = ", notificationMessage.sequenceNumber.toString(), (_a = notificationMessage.notificationData) === null || _a === void 0 ? void 0 : _a.map((x) => x === null || x === void 0 ? void 0 : x.constructor.name).join(" "));
969
- // debugLog(notificationMessage.toString());
970
- }
971
- if (this.state !== SubscriptionState.CLOSED) {
972
- (0, node_opcua_assert_1.assert)(notificationMessage.notificationData.length > 0, "We are not expecting a keep-alive message here");
973
- this.state = SubscriptionState.NORMAL;
974
- debugLog("subscription " + this.id + chalk.bgYellow(" set to NORMAL"));
975
- }
976
- }
977
- process_subscription() {
978
- (0, node_opcua_assert_1.assert)(this.publishEngine.pendingPublishRequestCount > 0);
979
- if (!this.publishingEnabled) {
980
- // no publish to do, except keep alive
981
- debugLog(" -> no publish to do, except keep alive");
982
- this._process_keepAlive();
983
- return;
984
- }
985
- if (!this.hasPendingNotifications && this.hasUncollectedMonitoredItemNotifications) {
986
- // collect notification from monitored items
987
- this._harvestMonitoredItems();
988
- }
989
- // let process them first
990
- if (this.hasPendingNotifications) {
991
- this._publish_pending_notifications();
992
- if (this.state === SubscriptionState.NORMAL && this.hasPendingNotifications) {
993
- // istanbul ignore next
994
- if (doDebug) {
995
- debugLog(" -> pendingPublishRequestCount > 0 " + "&& normal state => re-trigger tick event immediately ");
996
- }
997
- // let process an new publish request
998
- setImmediate(this._tick.bind(this));
999
- }
1000
- }
1001
- else {
1002
- this._process_keepAlive();
1003
- }
1004
- }
1005
- _get_future_sequence_number() {
1006
- return this._sequence_number_generator ? this._sequence_number_generator.future() : 0;
1007
- }
1008
- _process_keepAlive() {
1009
- this.increaseKeepAliveCounter();
1010
- if (this.keepAliveCounterHasExpired) {
1011
- debugLog(` -> _process_keepAlive => keepAliveCounterHasExpired`);
1012
- if (this._sendKeepAliveResponse()) {
1013
- this.resetLifeTimeAndKeepAliveCounters();
1014
- }
1015
- else {
1016
- debugLog(" -> subscription.state === LATE , " +
1017
- "because keepAlive Response cannot be send due to lack of PublishRequest");
1018
- if (this.messageSent || this.keepAliveCounterHasExpired) {
1019
- this.state = SubscriptionState.LATE;
1020
- }
1021
- }
1022
- }
1023
- }
1024
- _stop_timer() {
1025
- if (this.timerId) {
1026
- debugLog(chalk.bgWhite.blue("Subscription#_stop_timer subscriptionId="), this.id);
1027
- clearInterval(this.timerId);
1028
- this.timerId = null;
1029
- }
1030
- }
1031
- _start_timer({ firstTime }) {
1032
- debugLog(chalk.bgWhite.blue("Subscription#_start_timer subscriptionId="), this.id, " publishingInterval = ", this.publishingInterval);
1033
- (0, node_opcua_assert_1.assert)(this.timerId === null);
1034
- // from the spec:
1035
- // When a Subscription is created, the first Message is sent at the end of the first publishing cycle to
1036
- // inform the Client that the Subscription is operational. A NotificationMessage is sent if there are
1037
- // Notifications ready to be reported. If there are none, a keep-alive Message is sent instead that
1038
- // contains a sequence number of 1, indicating that the first NotificationMessage has not yet been sent.
1039
- // This is the only time a keep-alive Message is sent without waiting for the maximum keep-alive count
1040
- // to be reached, as specified in (f) above.
1041
- // make sure that a keep-alive Message will be send at the end of the first publishing cycle
1042
- // if there are no Notifications ready.
1043
- this._keep_alive_counter = 0; // this.maxKeepAliveCount;
1044
- if (firstTime) {
1045
- (0, node_opcua_assert_1.assert)(this.messageSent === false);
1046
- (0, node_opcua_assert_1.assert)(this.state === SubscriptionState.CREATING);
1047
- }
1048
- (0, node_opcua_assert_1.assert)(this.publishingInterval >= Subscription.minimumPublishingInterval);
1049
- this.timerId = setInterval(this._tick.bind(this), this.publishingInterval);
1050
- }
1051
- // counter
1052
- _get_next_sequence_number() {
1053
- return this._sequence_number_generator ? this._sequence_number_generator.next() : 0;
1054
- }
1055
- /**
1056
- * @private
1057
- */
1058
- _tick() {
1059
- // istanbul ignore next
1060
- if (doDebug) {
1061
- debugLog(`Subscription#_tick id ${this.id} aborted=${this.aborted} state=${SubscriptionState[this.state]}`);
1062
- }
1063
- if (this.state === SubscriptionState.CLOSED) {
1064
- warningLog(`Warning: Subscription#_tick id ${this.id} called while subscription is CLOSED`);
1065
- return;
1066
- }
1067
- this.discardOldSentNotifications();
1068
- // istanbul ignore next
1069
- if (doDebug) {
1070
- debugLog(t(new Date()) + " " + this._life_time_counter + "/" + this.lifeTimeCount + chalk.cyan(" Subscription#_tick"), " processing subscriptionId=", this.id, "hasUncollectedMonitoredItemNotifications = ", this.hasUncollectedMonitoredItemNotifications, " publishingIntervalCount =", this.publishIntervalCount);
1071
- }
1072
- // give a chance to the publish engine to cancel timed out publish requests
1073
- this.publishEngine._on_tick();
1074
- this.publishIntervalCount += 1;
1075
- if (this.state === SubscriptionState.LATE) {
1076
- this.increaseLifeTimeCounter();
1077
- }
1078
- if (this.lifeTimeHasExpired) {
1079
- /* istanbul ignore next */
1080
- doDebug && debugLog(chalk.red.bold(`Subscription ${this.id} has expired !!!!! => Terminating`));
1081
- /**
1082
- * notify the subscription owner that the subscription has expired by exceeding its life time.
1083
- * @event expired
1084
- *
1085
- */
1086
- this.emit("expired");
1087
- // notify new terminated status only when subscription has timeout.
1088
- doDebug && debugLog("adding StatusChangeNotification notification message for BadTimeout subscription = ", this.id);
1089
- this._addNotificationMessage(new node_opcua_service_subscription_2.StatusChangeNotification({ status: node_opcua_status_code_1.StatusCodes.BadTimeout }));
1090
- // kill timer and delete monitored items and transfer pending notification messages
1091
- this.terminate();
1092
- return;
1093
- }
1094
- const publishEngine = this.publishEngine;
1095
- // istanbul ignore next
1096
- doDebug && debugLog("Subscription#_tick self._pending_notifications= ", this._pending_notifications.size);
1097
- if (publishEngine.pendingPublishRequestCount === 0 &&
1098
- (this.hasPendingNotifications || this.hasUncollectedMonitoredItemNotifications)) {
1099
- // istanbul ignore next
1100
- doDebug &&
1101
- debugLog("subscription set to LATE hasPendingNotifications = ", this.hasPendingNotifications, " hasUncollectedMonitoredItemNotifications =", this.hasUncollectedMonitoredItemNotifications);
1102
- this.state = SubscriptionState.LATE;
1103
- return;
1104
- }
1105
- if (publishEngine.pendingPublishRequestCount > 0) {
1106
- if (this.hasPendingNotifications) {
1107
- // simply pop pending notification and send it
1108
- this.process_subscription();
1109
- }
1110
- else if (this.hasUncollectedMonitoredItemNotifications) {
1111
- this.process_subscription();
1112
- }
1113
- else {
1114
- this._process_keepAlive();
1115
- }
1116
- }
1117
- else {
1118
- if (this.state !== SubscriptionState.LATE) {
1119
- this._process_keepAlive();
1120
- }
1121
- else {
1122
- this.resetKeepAliveCounter();
1123
- }
1124
- }
1125
- }
1126
- /**
1127
- * @private
1128
- */
1129
- _sendKeepAliveResponse() {
1130
- const future_sequence_number = this._get_future_sequence_number();
1131
- if (this.publishEngine.send_keep_alive_response(this.id, future_sequence_number)) {
1132
- this.messageSent = true;
1133
- // istanbul ignore next
1134
- doDebug &&
1135
- debugLog(` -> Subscription#_sendKeepAliveResponse subscriptionId ${this.id} future_sequence_number ${future_sequence_number}`);
1136
- /**
1137
- * notify the subscription owner that a keepalive message has to be sent.
1138
- * @event keepalive
1139
- *
1140
- */
1141
- this.emit("keepalive", future_sequence_number);
1142
- this.state = SubscriptionState.KEEPALIVE;
1143
- return true;
1144
- }
1145
- return false;
1146
- }
1147
- /**
1148
- * Reset the Lifetime Counter Variable to the value specified for the lifetime of a Subscription in
1149
- * the CreateSubscription Service( 5.13.2).
1150
- * @private
1151
- */
1152
- resetKeepAliveCounter() {
1153
- this._keep_alive_counter = 0;
1154
- // istanbul ignore next
1155
- doDebug &&
1156
- debugLog(" -> subscriptionId", this.id, " Resetting keepAliveCounter = ", this._keep_alive_counter, this.maxKeepAliveCount);
1157
- }
1158
- /**
1159
- * @private
1160
- */
1161
- increaseKeepAliveCounter() {
1162
- this._keep_alive_counter += 1;
1163
- // istanbul ignore next
1164
- doDebug &&
1165
- debugLog(" -> subscriptionId", this.id, " Increasing keepAliveCounter = ", this._keep_alive_counter, this.maxKeepAliveCount);
1166
- }
1167
- /**
1168
- * @private
1169
- */
1170
- _addNotificationMessage(notificationData, monitoredItemId) {
1171
- // istanbul ignore next
1172
- doDebug && debugLog(chalk.yellow("Subscription#_addNotificationMessage"), notificationData.toString());
1173
- this._pending_notifications.push({
1174
- monitoredItemId,
1175
- notification: notificationData,
1176
- publishTime: new Date(),
1177
- start_tick: this.publishIntervalCount
1178
- });
1179
- }
1180
- /**
1181
- * @internal
1182
- * @param monitoredItemId
1183
- */
1184
- _removePendingNotificationsFor(monitoredItemId) {
1185
- const nbRemovedNotification = this._pending_notifications.filterOut((e) => e.monitoredItemId === monitoredItemId);
1186
- doDebug && debugLog(`Removed ${nbRemovedNotification} notifications`);
1187
- }
1188
- /**
1189
- * Extract the next Notification that is ready to be sent to the client.
1190
- * @return the Notification to send._pending_notifications
1191
- */
1192
- _popNotificationToSend() {
1193
- (0, node_opcua_assert_1.assert)(this._pending_notifications.size > 0);
1194
- const notificationMessage = new node_opcua_service_subscription_2.NotificationMessage({
1195
- sequenceNumber: 0xffffffff,
1196
- notificationData: [],
1197
- publishTime: new Date()
1198
- }); //
1199
- const dataChangeNotifications = new node_opcua_service_subscription_2.DataChangeNotification({
1200
- monitoredItems: []
1201
- });
1202
- const eventNotificationList = new node_opcua_service_subscription_2.EventNotificationList({
1203
- events: []
1204
- });
1205
- let statusChangeNotification;
1206
- let i = 0;
1207
- let hasEventFieldList = 0;
1208
- let hasMonitoredItemNotification = 0;
1209
- const m = this.maxNotificationsPerPublish;
1210
- while (i < m && this._pending_notifications.size > 0) {
1211
- if (hasEventFieldList || hasMonitoredItemNotification) {
1212
- const notification1 = this._pending_notifications.first().notification;
1213
- if (notification1 instanceof node_opcua_service_subscription_2.StatusChangeNotification) {
1214
- break;
1215
- }
1216
- }
1217
- const notification = this._pending_notifications.shift().notification;
1218
- if (notification instanceof node_opcua_service_subscription_2.MonitoredItemNotification) {
1219
- (0, node_opcua_assert_1.assert)(notification.clientHandle !== 4294967295);
1220
- dataChangeNotifications.monitoredItems.push(notification);
1221
- hasMonitoredItemNotification = 1;
1222
- }
1223
- else if (notification instanceof node_opcua_types_1.EventFieldList) {
1224
- eventNotificationList.events.push(notification);
1225
- hasEventFieldList = 1;
1226
- }
1227
- else if (notification instanceof node_opcua_service_subscription_2.StatusChangeNotification) {
1228
- // to do
1229
- statusChangeNotification = notification;
1230
- break;
1231
- }
1232
- i += 1;
1233
- }
1234
- if (dataChangeNotifications.monitoredItems.length) {
1235
- notificationMessage.notificationData.push(dataChangeNotifications);
1236
- }
1237
- if (eventNotificationList.events.length) {
1238
- notificationMessage.notificationData.push(eventNotificationList);
1239
- }
1240
- if (statusChangeNotification) {
1241
- notificationMessage.notificationData.push(statusChangeNotification);
1242
- }
1243
- return notificationMessage;
1244
- }
1245
- /**
1246
- * discardOldSentNotification find all sent notification message that have expired keep-alive
1247
- * and destroy them.
1248
- * @private
1249
- *
1250
- * Subscriptions maintain a retransmission queue of sent NotificationMessages.
1251
- * NotificationMessages are retained in this queue until they are acknowledged or until they have
1252
- * been in the queue for a minimum of one keep-alive interval.
1253
- *
1254
- */
1255
- discardOldSentNotifications() {
1256
- // Sessions maintain a retransmission queue of sent NotificationMessages. NotificationMessages
1257
- // are retained in this queue until they are acknowledged. The Session shall maintain a
1258
- // retransmission queue size of at least two times the number of Publish requests per Session the
1259
- // Server supports. Clients are required to acknowledge NotificationMessages as they are received. In the
1260
- // case of a retransmission queue overflow, the oldest sent NotificationMessage gets deleted. If a
1261
- // Subscription is transferred to another Session, the queued NotificationMessages for this
1262
- // Subscription are moved from the old to the new Session.
1263
- if (maxNotificationMessagesInQueue <= this._sent_notification_messages.length) {
1264
- doDebug && debugLog("discardOldSentNotifications = ", this._sent_notification_messages.length);
1265
- this._sent_notification_messages.splice(this._sent_notification_messages.length - maxNotificationMessagesInQueue);
1266
- }
1267
- }
1268
- /**
1269
- * @param timestampsToReturn
1270
- * @param monitoredItemCreateRequest
1271
- * @param node
1272
- * @private
1273
- */
1274
- _createMonitoredItemStep2(timestampsToReturn, monitoredItemCreateRequest, node) {
1275
- // note : most of the parameter inconsistencies shall have been handled by the caller
1276
- // any error here will raise an assert here
1277
- (0, node_opcua_assert_1.assert)(monitoredItemCreateRequest instanceof node_opcua_service_subscription_2.MonitoredItemCreateRequest);
1278
- const itemToMonitor = monitoredItemCreateRequest.itemToMonitor;
1279
- // xx check if attribute Id invalid (we only support Value or EventNotifier )
1280
- // xx assert(itemToMonitor.attributeId !== AttributeIds.INVALID);
1281
- this.monitoredItemIdCounter += 1;
1282
- const monitoredItemId = getNextMonitoredItemId();
1283
- const requestedParameters = monitoredItemCreateRequest.requestedParameters;
1284
- // adjust requestedParameters.samplingInterval
1285
- requestedParameters.samplingInterval = this.adjustSamplingInterval(requestedParameters.samplingInterval, node);
1286
- // reincorporate monitoredItemId and itemToMonitor into the requestedParameters
1287
- const options = requestedParameters;
1288
- options.monitoredItemId = monitoredItemId;
1289
- options.itemToMonitor = itemToMonitor;
1290
- const monitoredItem = new monitored_item_1.MonitoredItem(options);
1291
- monitoredItem.timestampsToReturn = timestampsToReturn;
1292
- monitoredItem.$subscription = this;
1293
- (0, node_opcua_assert_1.assert)(monitoredItem.monitoredItemId === monitoredItemId);
1294
- this.monitoredItems[monitoredItemId] = monitoredItem;
1295
- this.globalCounter.totalMonitoredItemCount += 1;
1296
- (0, node_opcua_assert_1.assert)(monitoredItem.clientHandle !== 4294967295);
1297
- const filterResult = _process_filter(node, requestedParameters.filter);
1298
- const monitoredItemCreateResult = new node_opcua_service_subscription_2.MonitoredItemCreateResult({
1299
- filterResult,
1300
- monitoredItemId,
1301
- revisedQueueSize: monitoredItem.queueSize,
1302
- revisedSamplingInterval: monitoredItem.samplingInterval,
1303
- statusCode: node_opcua_status_code_1.StatusCodes.Good
1304
- });
1305
- // this.emit("monitoredItem", monitoredItem, itemToMonitor);
1306
- return monitoredItemCreateResult;
1307
- }
1308
- /**
1309
- *
1310
- * @param monitoredItem
1311
- * @param monitoredItemCreateRequest
1312
- * @private
1313
- */
1314
- _createMonitoredItemStep3(monitoredItem, monitoredItemCreateRequest) {
1315
- if (!monitoredItem) {
1316
- return;
1317
- }
1318
- (0, node_opcua_assert_1.assert)(monitoredItem.monitoringMode === node_opcua_service_subscription_2.MonitoringMode.Invalid);
1319
- (0, node_opcua_assert_1.assert)(typeof monitoredItem.samplingFunc === "function", " expecting a sampling function here");
1320
- const monitoringMode = monitoredItemCreateRequest.monitoringMode; // Disabled, Sampling, Reporting
1321
- monitoredItem.setMonitoringMode(monitoringMode);
1322
- }
1323
- _harvestMonitoredItems() {
1324
- for (const monitoredItem of Object.values(this.monitoredItems)) {
1325
- const notifications_chunks = monitoredItem.extractMonitoredItemNotifications();
1326
- for (const chunk of notifications_chunks) {
1327
- this._addNotificationMessage(chunk, monitoredItem.monitoredItemId);
1328
- }
1329
- }
1330
- this._hasUncollectedMonitoredItemNotifications = false;
1331
- }
1332
- }
1333
- exports.Subscription = Subscription;
1334
- Subscription.minimumPublishingInterval = 50; // fastest possible
1335
- Subscription.defaultPublishingInterval = 1000; // one second
1336
- Subscription.maximumPublishingInterval = 1000 * 60; // one minute
1337
- Subscription.maxNotificationPerPublishHighLimit = 1000;
1338
- Subscription.minimumLifetimeDuration = 5 * 1000; // // we want 2 seconds minimum lifetime for any subscription
1339
- Subscription.maximumLifetimeDuration = 60 * 60 * 1000; // 1 hour
1340
- /**
1341
- * maximum number of monitored item in a subscription to be used
1342
- * when serverCapacity.maxMonitoredItems and serverCapacity.maxMonitoredItemsPerSubscription are not set.
1343
- */
1344
- Subscription.defaultMaxMonitoredItemCount = 20000;
1345
- Subscription.registry = new node_opcua_object_registry_1.ObjectRegistry();
1346
- (0, node_opcua_assert_1.assert)(Subscription.maximumPublishingInterval < 2147483647, "maximumPublishingInterval cannot exceed (2**31-1) ms ");
1
+ "use strict";
2
+ /**
3
+ * @module node-opcua-server
4
+ */
5
+ // tslint:disable:no-console
6
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
7
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
8
+ return new (P || (P = Promise))(function (resolve, reject) {
9
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
10
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
11
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
12
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
13
+ });
14
+ };
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.Subscription = exports.SubscriptionState = void 0;
17
+ const events_1 = require("events");
18
+ const chalk = require("chalk");
19
+ const node_opcua_address_space_1 = require("node-opcua-address-space");
20
+ const node_opcua_assert_1 = require("node-opcua-assert");
21
+ const node_opcua_common_1 = require("node-opcua-common");
22
+ const node_opcua_data_model_1 = require("node-opcua-data-model");
23
+ const node_opcua_debug_1 = require("node-opcua-debug");
24
+ const node_opcua_nodeid_1 = require("node-opcua-nodeid");
25
+ const node_opcua_object_registry_1 = require("node-opcua-object-registry");
26
+ const node_opcua_secure_channel_1 = require("node-opcua-secure-channel");
27
+ const node_opcua_service_filter_1 = require("node-opcua-service-filter");
28
+ const node_opcua_service_subscription_1 = require("node-opcua-service-subscription");
29
+ const node_opcua_service_subscription_2 = require("node-opcua-service-subscription");
30
+ const node_opcua_status_code_1 = require("node-opcua-status-code");
31
+ const node_opcua_types_1 = require("node-opcua-types");
32
+ const queue_1 = require("./queue");
33
+ const monitored_item_1 = require("./monitored_item");
34
+ const validate_filter_1 = require("./validate_filter");
35
+ const i_server_side_publish_engine_1 = require("./i_server_side_publish_engine");
36
+ const debugLog = (0, node_opcua_debug_1.make_debugLog)(__filename);
37
+ const doDebug = (0, node_opcua_debug_1.checkDebugFlag)(__filename);
38
+ const warningLog = (0, node_opcua_debug_1.make_warningLog)(__filename);
39
+ const maxNotificationMessagesInQueue = 100;
40
+ var SubscriptionState;
41
+ (function (SubscriptionState) {
42
+ SubscriptionState[SubscriptionState["CLOSED"] = 1] = "CLOSED";
43
+ SubscriptionState[SubscriptionState["CREATING"] = 2] = "CREATING";
44
+ SubscriptionState[SubscriptionState["NORMAL"] = 3] = "NORMAL";
45
+ // The keep-alive counter is not used in this state.
46
+ SubscriptionState[SubscriptionState["LATE"] = 4] = "LATE";
47
+ // ready to be sent, but there are no Publish requests queued. When in this state, the next Publish
48
+ // request is processed when it is received. The keep-alive counter is not used in this state.
49
+ SubscriptionState[SubscriptionState["KEEPALIVE"] = 5] = "KEEPALIVE";
50
+ // alive counter to count down to 0 from its maximum.
51
+ SubscriptionState[SubscriptionState["TERMINATED"] = 6] = "TERMINATED";
52
+ })(SubscriptionState = exports.SubscriptionState || (exports.SubscriptionState = {}));
53
+ function _adjust_publishing_interval(publishingInterval) {
54
+ publishingInterval =
55
+ publishingInterval === undefined || Number.isNaN(publishingInterval)
56
+ ? Subscription.defaultPublishingInterval
57
+ : publishingInterval;
58
+ publishingInterval = Math.max(publishingInterval, Subscription.minimumPublishingInterval);
59
+ publishingInterval = Math.min(publishingInterval, Subscription.maximumPublishingInterval);
60
+ return publishingInterval;
61
+ }
62
+ const minimumMaxKeepAliveCount = 2;
63
+ const maximumMaxKeepAliveCount = 12000;
64
+ function _adjust_maxKeepAliveCount(maxKeepAliveCount /*,publishingInterval*/) {
65
+ maxKeepAliveCount = maxKeepAliveCount || minimumMaxKeepAliveCount;
66
+ maxKeepAliveCount = Math.max(maxKeepAliveCount, minimumMaxKeepAliveCount);
67
+ maxKeepAliveCount = Math.min(maxKeepAliveCount, maximumMaxKeepAliveCount);
68
+ return maxKeepAliveCount;
69
+ }
70
+ function _adjust_lifeTimeCount(lifeTimeCount, maxKeepAliveCount, publishingInterval) {
71
+ lifeTimeCount = lifeTimeCount || 1;
72
+ // let's make sure that lifeTimeCount is at least three time maxKeepAliveCount
73
+ // Note : the specs say ( part 3 - CreateSubscriptionParameter )
74
+ // "The lifetime count shall be a minimum of three times the keep keep-alive count."
75
+ lifeTimeCount = Math.max(lifeTimeCount, maxKeepAliveCount * 3);
76
+ const minTicks = Math.ceil(Subscription.minimumLifetimeDuration / publishingInterval);
77
+ const maxTicks = Math.floor(Subscription.maximumLifetimeDuration / publishingInterval);
78
+ lifeTimeCount = Math.max(minTicks, lifeTimeCount);
79
+ lifeTimeCount = Math.min(maxTicks, lifeTimeCount);
80
+ return lifeTimeCount;
81
+ }
82
+ function _adjust_publishingEnable(publishingEnabled) {
83
+ return publishingEnabled === null || publishingEnabled === undefined ? true : !!publishingEnabled;
84
+ }
85
+ function _adjust_maxNotificationsPerPublish(maxNotificationsPerPublish) {
86
+ (0, node_opcua_assert_1.assert)(Subscription.maxNotificationPerPublishHighLimit > 0, "Subscription.maxNotificationPerPublishHighLimit must be positive");
87
+ maxNotificationsPerPublish = maxNotificationsPerPublish || 0;
88
+ (0, node_opcua_assert_1.assert)(typeof maxNotificationsPerPublish === "number");
89
+ // must be strictly positive
90
+ maxNotificationsPerPublish = maxNotificationsPerPublish >= 0 ? maxNotificationsPerPublish : 0;
91
+ if (maxNotificationsPerPublish === 0) {
92
+ // if zero then => use our HighLimit
93
+ maxNotificationsPerPublish = Subscription.maxNotificationPerPublishHighLimit;
94
+ }
95
+ else {
96
+ // if not zero then should be capped by maxNotificationPerPublishHighLimit
97
+ maxNotificationsPerPublish = Math.min(Subscription.maxNotificationPerPublishHighLimit, maxNotificationsPerPublish);
98
+ }
99
+ (0, node_opcua_assert_1.assert)(maxNotificationsPerPublish !== 0 && maxNotificationsPerPublish <= Subscription.maxNotificationPerPublishHighLimit);
100
+ return maxNotificationsPerPublish;
101
+ }
102
+ function w(s, length) {
103
+ return ("000" + s).padStart(length);
104
+ }
105
+ function t(d) {
106
+ return w(d.getHours(), 2) + ":" + w(d.getMinutes(), 2) + ":" + w(d.getSeconds(), 2) + ":" + w(d.getMilliseconds(), 3);
107
+ }
108
+ function _getSequenceNumbers(arr) {
109
+ return arr.map((notificationMessage) => notificationMessage.sequenceNumber);
110
+ }
111
+ function analyseEventFilterResult(node, eventFilter) {
112
+ /* istanbul ignore next */
113
+ if (!(eventFilter instanceof node_opcua_service_filter_1.EventFilter)) {
114
+ throw new Error("Internal Error");
115
+ }
116
+ const selectClauseResults = (0, node_opcua_service_filter_1.checkSelectClauses)(node, eventFilter.selectClauses || []);
117
+ const whereClauseResult = new node_opcua_types_1.ContentFilterResult();
118
+ return new node_opcua_types_1.EventFilterResult({
119
+ selectClauseDiagnosticInfos: [],
120
+ selectClauseResults,
121
+ whereClauseResult
122
+ });
123
+ }
124
+ function analyseDataChangeFilterResult(node, dataChangeFilter) {
125
+ (0, node_opcua_assert_1.assert)(dataChangeFilter instanceof node_opcua_service_subscription_2.DataChangeFilter);
126
+ // the opcua specification doesn't provide dataChangeFilterResult
127
+ return null;
128
+ }
129
+ function analyseAggregateFilterResult(node, aggregateFilter) {
130
+ (0, node_opcua_assert_1.assert)(aggregateFilter instanceof node_opcua_service_subscription_1.AggregateFilter);
131
+ return new node_opcua_types_1.AggregateFilterResult({});
132
+ }
133
+ function _process_filter(node, filter) {
134
+ if (!filter) {
135
+ return null;
136
+ }
137
+ if (filter instanceof node_opcua_service_filter_1.EventFilter) {
138
+ return analyseEventFilterResult(node, filter);
139
+ }
140
+ else if (filter instanceof node_opcua_service_subscription_2.DataChangeFilter) {
141
+ return analyseDataChangeFilterResult(node, filter);
142
+ }
143
+ else if (filter instanceof node_opcua_service_subscription_1.AggregateFilter) {
144
+ return analyseAggregateFilterResult(node, filter);
145
+ }
146
+ // istanbul ignore next
147
+ throw new Error("invalid filter");
148
+ }
149
+ /**
150
+ * @private
151
+ */
152
+ function createSubscriptionDiagnostics(subscription) {
153
+ (0, node_opcua_assert_1.assert)(subscription instanceof Subscription);
154
+ const subscriptionDiagnostics = new node_opcua_common_1.SubscriptionDiagnosticsDataType({});
155
+ const subscription_subscriptionDiagnostics = subscriptionDiagnostics;
156
+ subscription_subscriptionDiagnostics.$subscription = subscription;
157
+ // "sessionId"
158
+ subscription_subscriptionDiagnostics.__defineGetter__("sessionId", function () {
159
+ if (!this.$subscription) {
160
+ return node_opcua_nodeid_1.NodeId.nullNodeId;
161
+ }
162
+ return this.$subscription.getSessionId();
163
+ });
164
+ subscription_subscriptionDiagnostics.__defineGetter__("subscriptionId", function () {
165
+ if (!this.$subscription) {
166
+ return 0;
167
+ }
168
+ return this.$subscription.id;
169
+ });
170
+ subscription_subscriptionDiagnostics.__defineGetter__("priority", function () {
171
+ if (!this.$subscription) {
172
+ return 0;
173
+ }
174
+ return this.$subscription.priority;
175
+ });
176
+ subscription_subscriptionDiagnostics.__defineGetter__("publishingInterval", function () {
177
+ if (!this.$subscription) {
178
+ return 0;
179
+ }
180
+ return this.$subscription.publishingInterval;
181
+ });
182
+ subscription_subscriptionDiagnostics.__defineGetter__("maxLifetimeCount", function () {
183
+ return this.$subscription.lifeTimeCount;
184
+ });
185
+ subscription_subscriptionDiagnostics.__defineGetter__("maxKeepAliveCount", function () {
186
+ if (!this.$subscription) {
187
+ return 0;
188
+ }
189
+ return this.$subscription.maxKeepAliveCount;
190
+ });
191
+ subscription_subscriptionDiagnostics.__defineGetter__("maxNotificationsPerPublish", function () {
192
+ if (!this.$subscription) {
193
+ return 0;
194
+ }
195
+ return this.$subscription.maxNotificationsPerPublish;
196
+ });
197
+ subscription_subscriptionDiagnostics.__defineGetter__("publishingEnabled", function () {
198
+ if (!this.$subscription) {
199
+ return false;
200
+ }
201
+ return this.$subscription.publishingEnabled;
202
+ });
203
+ subscription_subscriptionDiagnostics.__defineGetter__("monitoredItemCount", function () {
204
+ if (!this.$subscription) {
205
+ return 0;
206
+ }
207
+ return this.$subscription.monitoredItemCount;
208
+ });
209
+ subscription_subscriptionDiagnostics.__defineGetter__("nextSequenceNumber", function () {
210
+ if (!this.$subscription) {
211
+ return 0;
212
+ }
213
+ return this.$subscription._get_future_sequence_number();
214
+ });
215
+ subscription_subscriptionDiagnostics.__defineGetter__("disabledMonitoredItemCount", function () {
216
+ if (!this.$subscription) {
217
+ return 0;
218
+ }
219
+ return this.$subscription.disabledMonitoredItemCount;
220
+ });
221
+ /* those member of self.subscriptionDiagnostics are handled directly
222
+
223
+ modifyCount
224
+ enableCount,
225
+ disableCount,
226
+ republishRequestCount,
227
+ notificationsCount,
228
+ publishRequestCount,
229
+ dataChangeNotificationsCount,
230
+ eventNotificationsCount,
231
+ */
232
+ /*
233
+ those members are not updated yet in the code :
234
+ "republishMessageRequestCount",
235
+ "republishMessageCount",
236
+ "transferRequestCount",
237
+ "transferredToAltClientCount",
238
+ "transferredToSameClientCount",
239
+ "latePublishRequestCount",
240
+ "unacknowledgedMessageCount",
241
+ "discardedMessageCount",
242
+ "monitoringQueueOverflowCount",
243
+ "eventQueueOverFlowCount"
244
+ */
245
+ subscription_subscriptionDiagnostics.__defineGetter__("currentKeepAliveCount", function () {
246
+ if (!this.$subscription) {
247
+ return 0;
248
+ }
249
+ return this.$subscription.currentKeepAliveCount;
250
+ });
251
+ subscription_subscriptionDiagnostics.__defineGetter__("currentLifetimeCount", function () {
252
+ if (!this.$subscription) {
253
+ return 0;
254
+ }
255
+ return this.$subscription.currentLifetimeCount;
256
+ });
257
+ // add object in Variable SubscriptionDiagnosticArray (i=2290) ( Array of SubscriptionDiagnostics)
258
+ // add properties in Variable to reflect
259
+ return subscriptionDiagnostics;
260
+ }
261
+ let g_monitoredItemId = Math.ceil(Math.random() * 100000);
262
+ function getNextMonitoredItemId() {
263
+ return g_monitoredItemId++;
264
+ }
265
+ // function myFilter<T>(t1: any, chunk: any[]): T[] {
266
+ // return chunk.filter(filter_instanceof.bind(null, t1));
267
+ // }
268
+ // function makeNotificationData(notifications_chunk: QueueItem): NotificationData {
269
+ // const dataChangedNotificationData = myFilter<MonitoredItemNotification>(MonitoredItemNotification, notifications_chunk);
270
+ // const eventNotificationListData = myFilter<EventFieldList>(EventFieldList, notifications_chunk);
271
+ // assert(notifications_chunk.length === dataChangedNotificationData.length + eventNotificationListData.length);
272
+ // const notifications: (DataChangeNotification | EventNotificationList)[] = [];
273
+ // // add dataChangeNotification
274
+ // if (dataChangedNotificationData.length) {
275
+ // const dataChangeNotification = new DataChangeNotification({
276
+ // diagnosticInfos: [],
277
+ // monitoredItems: dataChangedNotificationData
278
+ // });
279
+ // notifications.push(dataChangeNotification);
280
+ // }
281
+ // // add dataChangeNotification
282
+ // if (eventNotificationListData.length) {
283
+ // const eventNotificationList = new EventNotificationList({
284
+ // events: eventNotificationListData
285
+ // });
286
+ // notifications.push(eventNotificationList);
287
+ // }
288
+ // return notifications.length === 0 ? null : notifications;
289
+ // }
290
+ const INVALID_ID = -1;
291
+ /**
292
+ * The Subscription class used in the OPCUA server side.
293
+ */
294
+ class Subscription extends events_1.EventEmitter {
295
+ constructor(options) {
296
+ super();
297
+ this._state = -1;
298
+ this._keep_alive_counter = 0;
299
+ this._hasUncollectedMonitoredItemNotifications = false;
300
+ options = options || {};
301
+ Subscription.registry.register(this);
302
+ (0, node_opcua_assert_1.assert)(this.sessionId instanceof node_opcua_nodeid_1.NodeId, "expecting a sessionId NodeId");
303
+ this.publishEngine = options.publishEngine;
304
+ this.id = options.id || INVALID_ID;
305
+ this.priority = options.priority || 0;
306
+ this.publishingInterval = _adjust_publishing_interval(options.publishingInterval);
307
+ this.maxKeepAliveCount = _adjust_maxKeepAliveCount(options.maxKeepAliveCount); // , this.publishingInterval);
308
+ this.resetKeepAliveCounter();
309
+ this.lifeTimeCount = _adjust_lifeTimeCount(options.lifeTimeCount || 0, this.maxKeepAliveCount, this.publishingInterval);
310
+ this.maxNotificationsPerPublish = _adjust_maxNotificationsPerPublish(options.maxNotificationsPerPublish);
311
+ this._life_time_counter = 0;
312
+ this.resetLifeTimeCounter();
313
+ // notification message that are ready to be sent to the client
314
+ this._pending_notifications = new queue_1.Queue();
315
+ this._sent_notification_messages = [];
316
+ this._sequence_number_generator = new node_opcua_secure_channel_1.SequenceNumberGenerator();
317
+ // initial state of the subscription
318
+ this.state = SubscriptionState.CREATING;
319
+ this.publishIntervalCount = 0;
320
+ this.monitoredItems = {}; // monitored item map
321
+ this.monitoredItemIdCounter = 0;
322
+ this.publishingEnabled = _adjust_publishingEnable(options.publishingEnabled);
323
+ this.subscriptionDiagnostics = createSubscriptionDiagnostics(this);
324
+ // A boolean value that is set to TRUE to mean that either a NotificationMessage or a keep-alive
325
+ // Message has been sent on the Subscription. It is a flag that is used to ensure that either a
326
+ // NotificationMessage or a keep-alive Message is sent out the first time the publishing
327
+ // timer expires.
328
+ this.messageSent = false;
329
+ this.timerId = null;
330
+ this._start_timer({ firstTime: true });
331
+ debugLog(chalk.green(`creating subscription ${this.id}`));
332
+ this.serverCapabilities = options.serverCapabilities;
333
+ this.serverCapabilities.maxMonitoredItems =
334
+ this.serverCapabilities.maxMonitoredItems || Subscription.defaultMaxMonitoredItemCount;
335
+ this.serverCapabilities.maxMonitoredItemsPerSubscription =
336
+ this.serverCapabilities.maxMonitoredItemsPerSubscription || Subscription.defaultMaxMonitoredItemCount;
337
+ this.globalCounter = options.globalCounter;
338
+ }
339
+ /**
340
+ * @deprecated use serverCapacity.maxMonitoredItems and serverCapacity.maxMonitoredItemsPerSubscription instead
341
+ */
342
+ static get maxMonitoredItemCount() {
343
+ return Subscription.defaultMaxMonitoredItemCount;
344
+ }
345
+ set state(value) {
346
+ if (this._state !== value) {
347
+ this._state = value;
348
+ this.emit("stateChanged", value);
349
+ }
350
+ }
351
+ get state() {
352
+ return this._state;
353
+ }
354
+ get sessionId() {
355
+ return this.$session ? this.$session.nodeId : node_opcua_nodeid_1.NodeId.nullNodeId;
356
+ }
357
+ get currentLifetimeCount() {
358
+ return this._life_time_counter;
359
+ }
360
+ get currentKeepAliveCount() {
361
+ return this._keep_alive_counter;
362
+ }
363
+ getSessionId() {
364
+ return this.sessionId;
365
+ }
366
+ toString() {
367
+ let str = "Subscription:\n";
368
+ str += " subscriptionId " + this.id + "\n";
369
+ str += " sessionId " + this.getSessionId().toString() + "\n";
370
+ str += " publishingEnabled " + this.publishingEnabled + "\n";
371
+ str += " maxKeepAliveCount " + this.maxKeepAliveCount + "\n";
372
+ str += " publishingInterval " + this.publishingInterval + "\n";
373
+ str += " lifeTimeCount " + this.lifeTimeCount + "\n";
374
+ str += " maxKeepAliveCount " + this.maxKeepAliveCount + "\n";
375
+ return str;
376
+ }
377
+ /**
378
+ * modify subscription parameters
379
+ * @param param
380
+ */
381
+ modify(param) {
382
+ // update diagnostic counter
383
+ this.subscriptionDiagnostics.modifyCount += 1;
384
+ const publishingInterval_old = this.publishingInterval;
385
+ param.requestedPublishingInterval = param.requestedPublishingInterval || 0;
386
+ param.requestedMaxKeepAliveCount = param.requestedMaxKeepAliveCount || this.maxKeepAliveCount;
387
+ param.requestedLifetimeCount = param.requestedLifetimeCount || this.lifeTimeCount;
388
+ this.publishingInterval = _adjust_publishing_interval(param.requestedPublishingInterval);
389
+ this.maxKeepAliveCount = _adjust_maxKeepAliveCount(param.requestedMaxKeepAliveCount);
390
+ this.lifeTimeCount = _adjust_lifeTimeCount(param.requestedLifetimeCount, this.maxKeepAliveCount, this.publishingInterval);
391
+ this.maxNotificationsPerPublish = _adjust_maxNotificationsPerPublish(param.maxNotificationsPerPublish || 0);
392
+ this.priority = param.priority || 0;
393
+ this.resetLifeTimeAndKeepAliveCounters();
394
+ if (publishingInterval_old !== this.publishingInterval) {
395
+ // todo
396
+ }
397
+ this._stop_timer();
398
+ this._start_timer({ firstTime: false });
399
+ }
400
+ /**
401
+ * set publishing mode
402
+ * @param publishingEnabled
403
+ */
404
+ setPublishingMode(publishingEnabled) {
405
+ this.publishingEnabled = !!publishingEnabled;
406
+ // update diagnostics
407
+ if (this.publishingEnabled) {
408
+ this.subscriptionDiagnostics.enableCount += 1;
409
+ }
410
+ else {
411
+ this.subscriptionDiagnostics.disableCount += 1;
412
+ }
413
+ this.resetLifeTimeCounter();
414
+ if (!publishingEnabled && this.state !== SubscriptionState.CLOSED) {
415
+ this.state = SubscriptionState.NORMAL;
416
+ }
417
+ return node_opcua_status_code_1.StatusCodes.Good;
418
+ }
419
+ /**
420
+ * @private
421
+ */
422
+ get keepAliveCounterHasExpired() {
423
+ return this._keep_alive_counter >= this.maxKeepAliveCount || this.state === SubscriptionState.LATE;
424
+ }
425
+ /**
426
+ * Reset the Lifetime Counter Variable to the value specified for the lifetime of a Subscription in
427
+ * the CreateSubscription Service( 5.13.2).
428
+ * @private
429
+ */
430
+ resetLifeTimeCounter() {
431
+ this._life_time_counter = 0;
432
+ }
433
+ /**
434
+ * @private
435
+ */
436
+ increaseLifeTimeCounter() {
437
+ this._life_time_counter += 1;
438
+ if (this._life_time_counter >= this.lifeTimeCount) {
439
+ this.emit("lifeTimeExpired");
440
+ }
441
+ this.emit("lifeTimeCounterChanged", this._life_time_counter);
442
+ }
443
+ /**
444
+ * True if the subscription life time has expired.
445
+ *
446
+ */
447
+ get lifeTimeHasExpired() {
448
+ (0, node_opcua_assert_1.assert)(this.lifeTimeCount > 0);
449
+ return this._life_time_counter >= this.lifeTimeCount;
450
+ }
451
+ /**
452
+ * number of milliseconds before this subscription times out (lifeTimeHasExpired === true);
453
+ */
454
+ get timeToExpiration() {
455
+ return (this.lifeTimeCount - this._life_time_counter) * this.publishingInterval;
456
+ }
457
+ get timeToKeepAlive() {
458
+ return (this.maxKeepAliveCount - this._keep_alive_counter) * this.publishingInterval;
459
+ }
460
+ /**
461
+ * Terminates the subscription.
462
+ * Calling this method will also remove any monitored items.
463
+ *
464
+ */
465
+ terminate() {
466
+ (0, node_opcua_assert_1.assert)(arguments.length === 0);
467
+ debugLog("Subscription#terminate status", SubscriptionState[this.state]);
468
+ if (this.state === SubscriptionState.CLOSED) {
469
+ // todo verify if asserting is required here
470
+ return;
471
+ }
472
+ // stop timer
473
+ this._stop_timer();
474
+ debugLog("terminating Subscription ", this.id, " with ", this.monitoredItemCount, " monitored items");
475
+ // dispose all monitoredItem
476
+ const keys = Object.keys(this.monitoredItems);
477
+ for (const key of keys) {
478
+ const status = this.removeMonitoredItem(parseInt(key, 10));
479
+ (0, node_opcua_assert_1.assert)(status === node_opcua_status_code_1.StatusCodes.Good);
480
+ }
481
+ (0, node_opcua_assert_1.assert)(this.monitoredItemCount === 0);
482
+ if (this.$session) {
483
+ this.$session._unexposeSubscriptionDiagnostics(this);
484
+ }
485
+ this.state = SubscriptionState.CLOSED;
486
+ /**
487
+ * notify the subscription owner that the subscription has been terminated.
488
+ * @event "terminated"
489
+ */
490
+ this.emit("terminated");
491
+ if (this.publishEngine) {
492
+ this.publishEngine.on_close_subscription(this);
493
+ }
494
+ }
495
+ setTriggering(triggeringItemId, linksToAdd, linksToRemove) {
496
+ /** Bad_NothingToDo, Bad_TooManyOperations,Bad_SubscriptionIdInvalid, Bad_MonitoredItemIdInvalid */
497
+ linksToAdd = linksToAdd || [];
498
+ linksToRemove = linksToRemove || [];
499
+ if (linksToAdd.length === 0 && linksToRemove.length === 0) {
500
+ return { statusCode: node_opcua_status_code_1.StatusCodes.BadNothingToDo, addResults: [], removeResults: [] };
501
+ }
502
+ const triggeringItem = this.getMonitoredItem(triggeringItemId);
503
+ const monitoredItemsToAdd = linksToAdd.map((id) => this.getMonitoredItem(id));
504
+ const monitoredItemsToRemove = linksToRemove.map((id) => this.getMonitoredItem(id));
505
+ if (!triggeringItem) {
506
+ const removeResults1 = monitoredItemsToRemove.map((m) => m ? node_opcua_status_code_1.StatusCodes.Good : node_opcua_status_code_1.StatusCodes.BadMonitoredItemIdInvalid);
507
+ const addResults1 = monitoredItemsToAdd.map((m) => m ? node_opcua_status_code_1.StatusCodes.Good : node_opcua_status_code_1.StatusCodes.BadMonitoredItemIdInvalid);
508
+ return {
509
+ statusCode: node_opcua_status_code_1.StatusCodes.BadMonitoredItemIdInvalid,
510
+ addResults: addResults1,
511
+ removeResults: removeResults1
512
+ };
513
+ }
514
+ //
515
+ // note: it seems that CTT imposed that we do remove before add
516
+ const removeResults = monitoredItemsToRemove.map((m) => !m ? node_opcua_status_code_1.StatusCodes.BadMonitoredItemIdInvalid : triggeringItem.removeLinkItem(m.monitoredItemId));
517
+ const addResults = monitoredItemsToAdd.map((m) => !m ? node_opcua_status_code_1.StatusCodes.BadMonitoredItemIdInvalid : triggeringItem.addLinkItem(m.monitoredItemId));
518
+ const statusCode = node_opcua_status_code_1.StatusCodes.Good;
519
+ // do binding
520
+ return {
521
+ statusCode,
522
+ addResults,
523
+ removeResults
524
+ };
525
+ }
526
+ dispose() {
527
+ if (doDebug) {
528
+ debugLog("Subscription#dispose", this.id, this.monitoredItemCount);
529
+ }
530
+ (0, node_opcua_assert_1.assert)(this.monitoredItemCount === 0, "MonitoredItems haven't been deleted first !!!");
531
+ (0, node_opcua_assert_1.assert)(this.timerId === null, "Subscription timer haven't been terminated");
532
+ if (this.subscriptionDiagnostics) {
533
+ this.subscriptionDiagnostics.$subscription = null;
534
+ }
535
+ this.publishEngine = undefined;
536
+ this._pending_notifications.clear();
537
+ this._sent_notification_messages = [];
538
+ this.$session = undefined;
539
+ this.removeAllListeners();
540
+ Subscription.registry.unregister(this);
541
+ }
542
+ get aborted() {
543
+ const session = this.$session;
544
+ if (!session) {
545
+ return true;
546
+ }
547
+ return session.aborted;
548
+ }
549
+ /**
550
+ * number of pending notifications
551
+ */
552
+ get pendingNotificationsCount() {
553
+ return this._pending_notifications ? this._pending_notifications.size : 0;
554
+ }
555
+ /**
556
+ * is 'true' if there are pending notifications for this subscription. (i.e moreNotifications)
557
+ */
558
+ get hasPendingNotifications() {
559
+ return this.pendingNotificationsCount > 0;
560
+ }
561
+ /**
562
+ * number of sent notifications
563
+ */
564
+ get sentNotificationMessageCount() {
565
+ return this._sent_notification_messages.length;
566
+ }
567
+ /**
568
+ * @internal
569
+ */
570
+ _flushSentNotifications() {
571
+ const tmp = this._sent_notification_messages;
572
+ this._sent_notification_messages = [];
573
+ return tmp;
574
+ }
575
+ /**
576
+ * number of monitored items handled by this subscription
577
+ */
578
+ get monitoredItemCount() {
579
+ return Object.keys(this.monitoredItems).length;
580
+ }
581
+ /**
582
+ * number of disabled monitored items.
583
+ */
584
+ get disabledMonitoredItemCount() {
585
+ return Object.values(this.monitoredItems).reduce((cumul, monitoredItem) => {
586
+ return cumul + (monitoredItem.monitoringMode === node_opcua_service_subscription_2.MonitoringMode.Disabled ? 1 : 0);
587
+ }, 0);
588
+ }
589
+ /**
590
+ * The number of unacknowledged messages saved in the republish queue.
591
+ */
592
+ get unacknowledgedMessageCount() {
593
+ return this.subscriptionDiagnostics.unacknowledgedMessageCount;
594
+ }
595
+ /**
596
+ * adjust monitored item sampling interval
597
+ * - an samplingInterval ===0 means that we use a event-base model ( no sampling)
598
+ * - otherwise the sampling is adjusted
599
+ * @private
600
+ */
601
+ adjustSamplingInterval(samplingInterval, node) {
602
+ if (samplingInterval < 0) {
603
+ // - The value -1 indicates that the default sampling interval defined by the publishing
604
+ // interval of the Subscription is requested.
605
+ // - Any negative number is interpreted as -1.
606
+ samplingInterval = this.publishingInterval;
607
+ }
608
+ else if (samplingInterval === 0) {
609
+ // OPCUA 1.0.3 Part 4 - 5.12.1.2
610
+ // The value 0 indicates that the Server should use the fastest practical rate.
611
+ // The fastest supported sampling interval may be equal to 0, which indicates
612
+ // that the data item is exception-based rather than being sampled at some period.
613
+ // An exception-based model means that the underlying system does not require
614
+ // sampling and reports data changes.
615
+ const dataValueSamplingInterval = node.readAttribute(node_opcua_address_space_1.SessionContext.defaultContext, node_opcua_data_model_1.AttributeIds.MinimumSamplingInterval);
616
+ // TODO if attributeId === AttributeIds.Value : sampling interval required here
617
+ if (dataValueSamplingInterval.statusCode === node_opcua_status_code_1.StatusCodes.Good) {
618
+ // node provides a Minimum sampling interval ...
619
+ samplingInterval = dataValueSamplingInterval.value.value;
620
+ (0, node_opcua_assert_1.assert)(samplingInterval >= 0 && samplingInterval <= monitored_item_1.MonitoredItem.maximumSamplingInterval);
621
+ // note : at this stage, a samplingInterval===0 means that the data item is really exception-based
622
+ }
623
+ }
624
+ else if (samplingInterval < monitored_item_1.MonitoredItem.minimumSamplingInterval) {
625
+ samplingInterval = monitored_item_1.MonitoredItem.minimumSamplingInterval;
626
+ }
627
+ else if (samplingInterval > monitored_item_1.MonitoredItem.maximumSamplingInterval) {
628
+ // If the requested samplingInterval is higher than the
629
+ // maximum sampling interval supported by the Server, the maximum sampling
630
+ // interval is returned.
631
+ samplingInterval = monitored_item_1.MonitoredItem.maximumSamplingInterval;
632
+ }
633
+ const node_minimumSamplingInterval = node && node.minimumSamplingInterval ? node.minimumSamplingInterval : 0;
634
+ samplingInterval = Math.max(samplingInterval, node_minimumSamplingInterval);
635
+ return samplingInterval;
636
+ }
637
+ /**
638
+ * create a monitored item
639
+ * @param addressSpace - address space
640
+ * @param timestampsToReturn - the timestamp to return
641
+ * @param monitoredItemCreateRequest - the parameters describing the monitored Item to create
642
+ */
643
+ preCreateMonitoredItem(addressSpace, timestampsToReturn, monitoredItemCreateRequest) {
644
+ (0, node_opcua_assert_1.assert)(monitoredItemCreateRequest instanceof node_opcua_service_subscription_2.MonitoredItemCreateRequest);
645
+ function handle_error(statusCode) {
646
+ return {
647
+ createResult: new node_opcua_service_subscription_2.MonitoredItemCreateResult({ statusCode }),
648
+ monitoredItemCreateRequest
649
+ };
650
+ }
651
+ const itemToMonitor = monitoredItemCreateRequest.itemToMonitor;
652
+ const node = addressSpace.findNode(itemToMonitor.nodeId);
653
+ if (!node) {
654
+ return handle_error(node_opcua_status_code_1.StatusCodes.BadNodeIdUnknown);
655
+ }
656
+ if (itemToMonitor.attributeId === node_opcua_data_model_1.AttributeIds.Value && !(node.nodeClass === node_opcua_data_model_1.NodeClass.Variable)) {
657
+ // AttributeIds.Value is only valid for monitoring value of UAVariables.
658
+ return handle_error(node_opcua_status_code_1.StatusCodes.BadAttributeIdInvalid);
659
+ }
660
+ if (itemToMonitor.attributeId === node_opcua_data_model_1.AttributeIds.INVALID) {
661
+ return handle_error(node_opcua_status_code_1.StatusCodes.BadAttributeIdInvalid);
662
+ }
663
+ if (!itemToMonitor.indexRange.isValid()) {
664
+ return handle_error(node_opcua_status_code_1.StatusCodes.BadIndexRangeInvalid);
665
+ }
666
+ // check dataEncoding applies only on Values
667
+ if (itemToMonitor.dataEncoding.name && itemToMonitor.attributeId !== node_opcua_data_model_1.AttributeIds.Value) {
668
+ return handle_error(node_opcua_status_code_1.StatusCodes.BadDataEncodingInvalid);
669
+ }
670
+ // check dataEncoding
671
+ if (!(0, node_opcua_data_model_1.isValidDataEncoding)(itemToMonitor.dataEncoding)) {
672
+ return handle_error(node_opcua_status_code_1.StatusCodes.BadDataEncodingUnsupported);
673
+ }
674
+ // check that item can be read by current user session
675
+ // filter
676
+ const requestedParameters = monitoredItemCreateRequest.requestedParameters;
677
+ const filter = requestedParameters.filter;
678
+ const statusCodeFilter = (0, validate_filter_1.validateFilter)(filter, itemToMonitor, node);
679
+ if (statusCodeFilter !== node_opcua_status_code_1.StatusCodes.Good) {
680
+ return handle_error(statusCodeFilter);
681
+ }
682
+ // do we have enough room for new monitored items ?
683
+ if (this.monitoredItemCount >= this.serverCapabilities.maxMonitoredItemsPerSubscription) {
684
+ return handle_error(node_opcua_status_code_1.StatusCodes.BadTooManyMonitoredItems);
685
+ }
686
+ if (this.globalCounter.totalMonitoredItemCount >= this.serverCapabilities.maxMonitoredItems) {
687
+ return handle_error(node_opcua_status_code_1.StatusCodes.BadTooManyMonitoredItems);
688
+ }
689
+ const createResult = this._createMonitoredItemStep2(timestampsToReturn, monitoredItemCreateRequest, node);
690
+ (0, node_opcua_assert_1.assert)(createResult.statusCode === node_opcua_status_code_1.StatusCodes.Good);
691
+ const monitoredItem = this.getMonitoredItem(createResult.monitoredItemId);
692
+ // istanbul ignore next
693
+ if (!monitoredItem) {
694
+ throw new Error("internal error");
695
+ }
696
+ // TODO: fix old way to set node. !!!!
697
+ monitoredItem.setNode(node);
698
+ this.emit("monitoredItem", monitoredItem, itemToMonitor);
699
+ return { monitoredItem, monitoredItemCreateRequest, createResult };
700
+ }
701
+ applyOnMonitoredItem(functor) {
702
+ return __awaiter(this, void 0, void 0, function* () {
703
+ for (const m of Object.values(this.monitoredItems)) {
704
+ yield functor(m);
705
+ }
706
+ });
707
+ }
708
+ postCreateMonitoredItem(monitoredItem, monitoredItemCreateRequest, createResult) {
709
+ this._createMonitoredItemStep3(monitoredItem, monitoredItemCreateRequest);
710
+ }
711
+ createMonitoredItem(addressSpace, timestampsToReturn, monitoredItemCreateRequest) {
712
+ const { monitoredItem, createResult } = this.preCreateMonitoredItem(addressSpace, timestampsToReturn, monitoredItemCreateRequest);
713
+ this.postCreateMonitoredItem(monitoredItem, monitoredItemCreateRequest, createResult);
714
+ return createResult;
715
+ }
716
+ /**
717
+ * get a monitoredItem by Id.
718
+ * @param monitoredItemId : the id of the monitored item to get.
719
+ * @return the monitored item matching monitoredItemId
720
+ */
721
+ getMonitoredItem(monitoredItemId) {
722
+ return this.monitoredItems[monitoredItemId] || null;
723
+ }
724
+ /**
725
+ * remove a monitored Item from the subscription.
726
+ * @param monitoredItemId : the id of the monitored item to get.
727
+ */
728
+ removeMonitoredItem(monitoredItemId) {
729
+ debugLog("Removing monitoredIem ", monitoredItemId);
730
+ if (!Object.prototype.hasOwnProperty.call(this.monitoredItems, monitoredItemId.toString())) {
731
+ return node_opcua_status_code_1.StatusCodes.BadMonitoredItemIdInvalid;
732
+ }
733
+ const monitoredItem = this.monitoredItems[monitoredItemId];
734
+ monitoredItem.terminate();
735
+ monitoredItem.dispose();
736
+ /**
737
+ *
738
+ * notify that a monitored item has been removed from the subscription
739
+ * @param monitoredItem {MonitoredItem}
740
+ */
741
+ this.emit("removeMonitoredItem", monitoredItem);
742
+ delete this.monitoredItems[monitoredItemId];
743
+ this.globalCounter.totalMonitoredItemCount -= 1;
744
+ this._removePendingNotificationsFor(monitoredItemId);
745
+ // flush pending notifications
746
+ // assert(this._pending_notifications.size === 0);
747
+ return node_opcua_status_code_1.StatusCodes.Good;
748
+ }
749
+ /**
750
+ * rue if monitored Item have uncollected Notifications
751
+ */
752
+ get hasUncollectedMonitoredItemNotifications() {
753
+ if (this._hasUncollectedMonitoredItemNotifications) {
754
+ return true;
755
+ }
756
+ const keys = Object.keys(this.monitoredItems);
757
+ const n = keys.length;
758
+ for (let i = 0; i < n; i++) {
759
+ const key = parseInt(keys[i], 10);
760
+ const monitoredItem = this.monitoredItems[key];
761
+ if (monitoredItem.hasMonitoredItemNotifications) {
762
+ this._hasUncollectedMonitoredItemNotifications = true;
763
+ return true;
764
+ }
765
+ }
766
+ return false;
767
+ }
768
+ get subscriptionId() {
769
+ return this.id;
770
+ }
771
+ getMessageForSequenceNumber(sequenceNumber) {
772
+ const notification_message = this._sent_notification_messages.find((e) => e.sequenceNumber === sequenceNumber);
773
+ return notification_message || null;
774
+ }
775
+ /**
776
+ * returns true if the notification has expired
777
+ * @param notification
778
+ */
779
+ notificationHasExpired(notification) {
780
+ (0, node_opcua_assert_1.assert)(Object.prototype.hasOwnProperty.call(notification, "start_tick"));
781
+ (0, node_opcua_assert_1.assert)(isFinite(notification.start_tick + this.maxKeepAliveCount));
782
+ return notification.start_tick + this.maxKeepAliveCount < this.publishIntervalCount;
783
+ }
784
+ /**
785
+ * returns in an array the sequence numbers of the notifications that have been sent
786
+ * and that haven't been acknowledged yet.
787
+ */
788
+ getAvailableSequenceNumbers() {
789
+ const availableSequenceNumbers = _getSequenceNumbers(this._sent_notification_messages);
790
+ return availableSequenceNumbers;
791
+ }
792
+ /**
793
+ * acknowledges a notification identified by its sequence number
794
+ */
795
+ acknowledgeNotification(sequenceNumber) {
796
+ debugLog("acknowledgeNotification ", sequenceNumber);
797
+ let foundIndex = -1;
798
+ this._sent_notification_messages.forEach((e, index) => {
799
+ if (e.sequenceNumber === sequenceNumber) {
800
+ foundIndex = index;
801
+ }
802
+ });
803
+ if (foundIndex === -1) {
804
+ if (doDebug) {
805
+ debugLog(chalk.red("acknowledging sequence FAILED !!! "), chalk.cyan(sequenceNumber.toString()));
806
+ }
807
+ return node_opcua_status_code_1.StatusCodes.BadSequenceNumberUnknown;
808
+ }
809
+ else {
810
+ if (doDebug) {
811
+ debugLog(chalk.yellow("acknowledging sequence "), chalk.cyan(sequenceNumber.toString()));
812
+ }
813
+ this._sent_notification_messages.splice(foundIndex, 1);
814
+ this.subscriptionDiagnostics.unacknowledgedMessageCount--;
815
+ return node_opcua_status_code_1.StatusCodes.Good;
816
+ }
817
+ }
818
+ /**
819
+ * getMonitoredItems is used to get information about monitored items of a subscription.Its intended
820
+ * use is defined in Part 4. This method is the implementation of the Standard OPCUA GetMonitoredItems Method.
821
+ * from spec:
822
+ * This method can be used to get the list of monitored items in a subscription if CreateMonitoredItems
823
+ * failed due to a network interruption and the client does not know if the creation succeeded in the server.
824
+ *
825
+ */
826
+ getMonitoredItems() {
827
+ const monitoredItems = Object.keys(this.monitoredItems);
828
+ const monitoredItemCount = monitoredItems.length;
829
+ const result = {
830
+ clientHandles: new Uint32Array(monitoredItemCount),
831
+ serverHandles: new Uint32Array(monitoredItemCount),
832
+ statusCode: node_opcua_status_code_1.StatusCodes.Good
833
+ };
834
+ for (let index = 0; index < monitoredItemCount; index++) {
835
+ const monitoredItemId = monitoredItems[index];
836
+ const serverHandle = parseInt(monitoredItemId, 10);
837
+ const monitoredItem = this.getMonitoredItem(serverHandle);
838
+ result.clientHandles[index] = monitoredItem.clientHandle;
839
+ // TODO: serverHandle is defined anywhere in the OPCUA Specification 1.02
840
+ // I am not sure what shall be reported for serverHandle...
841
+ // using monitoredItem.monitoredItemId instead...
842
+ // May be a clarification in the OPCUA Spec is required.
843
+ result.serverHandles[index] = serverHandle;
844
+ }
845
+ return result;
846
+ }
847
+ /**
848
+ * @private
849
+ */
850
+ resendInitialValues() {
851
+ return __awaiter(this, void 0, void 0, function* () {
852
+ const promises = [];
853
+ for (const monitoredItem of Object.values(this.monitoredItems)) {
854
+ (0, node_opcua_assert_1.assert)(monitoredItem.clientHandle !== 4294967295);
855
+ promises.push(monitoredItem.resendInitialValues());
856
+ }
857
+ yield Promise.all(promises);
858
+ this._harvestMonitoredItems();
859
+ });
860
+ }
861
+ /**
862
+ * @private
863
+ */
864
+ notifyTransfer() {
865
+ var _a;
866
+ // OPCUA UA Spec 1.0.3 : part 3 - page 82 - 5.13.7 TransferSubscriptions:
867
+ // If the Server transfers the Subscription to the new Session, the Server shall issue
868
+ // a StatusChangeNotification notificationMessage with the status code
869
+ // Good_SubscriptionTransferred to the old Session.
870
+ debugLog(chalk.red(" Subscription => Notifying Transfer "));
871
+ const notificationData = new node_opcua_service_subscription_2.StatusChangeNotification({
872
+ status: node_opcua_status_code_1.StatusCodes.GoodSubscriptionTransferred
873
+ });
874
+ if (this.publishEngine.pendingPublishRequestCount) {
875
+ // the GoodSubscriptionTransferred can be processed immediately
876
+ this._addNotificationMessage(notificationData);
877
+ debugLog(chalk.red("pendingPublishRequestCount"), (_a = this.publishEngine) === null || _a === void 0 ? void 0 : _a.pendingPublishRequestCount);
878
+ this._publish_pending_notifications();
879
+ }
880
+ else {
881
+ debugLog(chalk.red("Cannot send GoodSubscriptionTransferred => lets create a TransferredSubscription "));
882
+ const ts = new i_server_side_publish_engine_1.TransferredSubscription({
883
+ generator: this._sequence_number_generator,
884
+ id: this.id,
885
+ publishEngine: this.publishEngine
886
+ });
887
+ ts._pending_notification = notificationData;
888
+ this.publishEngine._closed_subscriptions.push(ts);
889
+ }
890
+ }
891
+ /**
892
+ *
893
+ * the server invokes the resetLifeTimeAndKeepAliveCounters method of the subscription
894
+ * when the server has send a Publish Response, so that the subscription
895
+ * can reset its life time counter.
896
+ *
897
+ * @private
898
+ */
899
+ resetLifeTimeAndKeepAliveCounters() {
900
+ this.resetLifeTimeCounter();
901
+ this.resetKeepAliveCounter();
902
+ }
903
+ _updateCounters(notificationMessage) {
904
+ for (const notificationData of notificationMessage.notificationData || []) {
905
+ // update diagnostics
906
+ if (notificationData instanceof node_opcua_service_subscription_2.DataChangeNotification) {
907
+ const nbNotifs = notificationData.monitoredItems.length;
908
+ this.subscriptionDiagnostics.dataChangeNotificationsCount += nbNotifs;
909
+ this.subscriptionDiagnostics.notificationsCount += nbNotifs;
910
+ }
911
+ else if (notificationData instanceof node_opcua_service_subscription_2.EventNotificationList) {
912
+ const nbNotifs = notificationData.events.length;
913
+ this.subscriptionDiagnostics.eventNotificationsCount += nbNotifs;
914
+ this.subscriptionDiagnostics.notificationsCount += nbNotifs;
915
+ }
916
+ else {
917
+ (0, node_opcua_assert_1.assert)(notificationData instanceof node_opcua_service_subscription_2.StatusChangeNotification);
918
+ // TODO
919
+ // note: :there is no way to count StatusChangeNotifications in opcua yet.
920
+ }
921
+ }
922
+ }
923
+ /**
924
+ * _publish_pending_notifications send a "notification" event:
925
+ *
926
+ * @private
927
+ * @precondition
928
+ * - pendingPublishRequestCount > 0
929
+ */
930
+ _publish_pending_notifications() {
931
+ var _a;
932
+ const publishEngine = this.publishEngine;
933
+ const subscriptionId = this.id;
934
+ // preconditions
935
+ (0, node_opcua_assert_1.assert)(publishEngine.pendingPublishRequestCount > 0);
936
+ (0, node_opcua_assert_1.assert)(this.hasPendingNotifications);
937
+ const notificationMessage = this._popNotificationToSend();
938
+ if (notificationMessage.notificationData.length === 0) {
939
+ return; // nothing to do
940
+ }
941
+ const moreNotifications = this.hasPendingNotifications;
942
+ this.emit("notification", notificationMessage);
943
+ // Update counters ....
944
+ this._updateCounters(notificationMessage);
945
+ (0, node_opcua_assert_1.assert)(Object.prototype.hasOwnProperty.call(notificationMessage, "sequenceNumber"));
946
+ (0, node_opcua_assert_1.assert)(Object.prototype.hasOwnProperty.call(notificationMessage, "notificationData"));
947
+ // update diagnostics
948
+ this.subscriptionDiagnostics.publishRequestCount += 1;
949
+ const response = new node_opcua_service_subscription_2.PublishResponse({
950
+ moreNotifications,
951
+ notificationMessage: {
952
+ notificationData: notificationMessage.notificationData,
953
+ sequenceNumber: this._get_next_sequence_number()
954
+ },
955
+ subscriptionId
956
+ });
957
+ this._sent_notification_messages.push(response.notificationMessage);
958
+ // get available sequence number;
959
+ const availableSequenceNumbers = this.getAvailableSequenceNumbers();
960
+ (0, node_opcua_assert_1.assert)(!response.notificationMessage ||
961
+ availableSequenceNumbers[availableSequenceNumbers.length - 1] === response.notificationMessage.sequenceNumber);
962
+ response.availableSequenceNumbers = availableSequenceNumbers;
963
+ publishEngine._send_response(this, response);
964
+ this.messageSent = true;
965
+ this.subscriptionDiagnostics.unacknowledgedMessageCount++;
966
+ this.resetLifeTimeAndKeepAliveCounters();
967
+ if (doDebug) {
968
+ debugLog("Subscription sending a notificationMessage subscriptionId=", subscriptionId, "sequenceNumber = ", notificationMessage.sequenceNumber.toString(), (_a = notificationMessage.notificationData) === null || _a === void 0 ? void 0 : _a.map((x) => x === null || x === void 0 ? void 0 : x.constructor.name).join(" "));
969
+ // debugLog(notificationMessage.toString());
970
+ }
971
+ if (this.state !== SubscriptionState.CLOSED) {
972
+ (0, node_opcua_assert_1.assert)(notificationMessage.notificationData.length > 0, "We are not expecting a keep-alive message here");
973
+ this.state = SubscriptionState.NORMAL;
974
+ debugLog("subscription " + this.id + chalk.bgYellow(" set to NORMAL"));
975
+ }
976
+ }
977
+ process_subscription() {
978
+ (0, node_opcua_assert_1.assert)(this.publishEngine.pendingPublishRequestCount > 0);
979
+ if (!this.publishingEnabled) {
980
+ // no publish to do, except keep alive
981
+ debugLog(" -> no publish to do, except keep alive");
982
+ this._process_keepAlive();
983
+ return;
984
+ }
985
+ if (!this.hasPendingNotifications && this.hasUncollectedMonitoredItemNotifications) {
986
+ // collect notification from monitored items
987
+ this._harvestMonitoredItems();
988
+ }
989
+ // let process them first
990
+ if (this.hasPendingNotifications) {
991
+ this._publish_pending_notifications();
992
+ if (this.state === SubscriptionState.NORMAL && this.hasPendingNotifications) {
993
+ // istanbul ignore next
994
+ if (doDebug) {
995
+ debugLog(" -> pendingPublishRequestCount > 0 " + "&& normal state => re-trigger tick event immediately ");
996
+ }
997
+ // let process an new publish request
998
+ setImmediate(this._tick.bind(this));
999
+ }
1000
+ }
1001
+ else {
1002
+ this._process_keepAlive();
1003
+ }
1004
+ }
1005
+ _get_future_sequence_number() {
1006
+ return this._sequence_number_generator ? this._sequence_number_generator.future() : 0;
1007
+ }
1008
+ _process_keepAlive() {
1009
+ this.increaseKeepAliveCounter();
1010
+ if (this.keepAliveCounterHasExpired) {
1011
+ debugLog(` -> _process_keepAlive => keepAliveCounterHasExpired`);
1012
+ if (this._sendKeepAliveResponse()) {
1013
+ this.resetLifeTimeAndKeepAliveCounters();
1014
+ }
1015
+ else {
1016
+ debugLog(" -> subscription.state === LATE , " +
1017
+ "because keepAlive Response cannot be send due to lack of PublishRequest");
1018
+ if (this.messageSent || this.keepAliveCounterHasExpired) {
1019
+ this.state = SubscriptionState.LATE;
1020
+ }
1021
+ }
1022
+ }
1023
+ }
1024
+ _stop_timer() {
1025
+ if (this.timerId) {
1026
+ debugLog(chalk.bgWhite.blue("Subscription#_stop_timer subscriptionId="), this.id);
1027
+ clearInterval(this.timerId);
1028
+ this.timerId = null;
1029
+ }
1030
+ }
1031
+ _start_timer({ firstTime }) {
1032
+ debugLog(chalk.bgWhite.blue("Subscription#_start_timer subscriptionId="), this.id, " publishingInterval = ", this.publishingInterval);
1033
+ (0, node_opcua_assert_1.assert)(this.timerId === null);
1034
+ // from the spec:
1035
+ // When a Subscription is created, the first Message is sent at the end of the first publishing cycle to
1036
+ // inform the Client that the Subscription is operational. A NotificationMessage is sent if there are
1037
+ // Notifications ready to be reported. If there are none, a keep-alive Message is sent instead that
1038
+ // contains a sequence number of 1, indicating that the first NotificationMessage has not yet been sent.
1039
+ // This is the only time a keep-alive Message is sent without waiting for the maximum keep-alive count
1040
+ // to be reached, as specified in (f) above.
1041
+ // make sure that a keep-alive Message will be send at the end of the first publishing cycle
1042
+ // if there are no Notifications ready.
1043
+ this._keep_alive_counter = 0; // this.maxKeepAliveCount;
1044
+ if (firstTime) {
1045
+ (0, node_opcua_assert_1.assert)(this.messageSent === false);
1046
+ (0, node_opcua_assert_1.assert)(this.state === SubscriptionState.CREATING);
1047
+ }
1048
+ (0, node_opcua_assert_1.assert)(this.publishingInterval >= Subscription.minimumPublishingInterval);
1049
+ this.timerId = setInterval(this._tick.bind(this), this.publishingInterval);
1050
+ }
1051
+ // counter
1052
+ _get_next_sequence_number() {
1053
+ return this._sequence_number_generator ? this._sequence_number_generator.next() : 0;
1054
+ }
1055
+ /**
1056
+ * @private
1057
+ */
1058
+ _tick() {
1059
+ // istanbul ignore next
1060
+ if (doDebug) {
1061
+ debugLog(`Subscription#_tick id ${this.id} aborted=${this.aborted} state=${SubscriptionState[this.state]}`);
1062
+ }
1063
+ if (this.state === SubscriptionState.CLOSED) {
1064
+ warningLog(`Warning: Subscription#_tick id ${this.id} called while subscription is CLOSED`);
1065
+ return;
1066
+ }
1067
+ this.discardOldSentNotifications();
1068
+ // istanbul ignore next
1069
+ if (doDebug) {
1070
+ debugLog(t(new Date()) + " " + this._life_time_counter + "/" + this.lifeTimeCount + chalk.cyan(" Subscription#_tick"), " processing subscriptionId=", this.id, "hasUncollectedMonitoredItemNotifications = ", this.hasUncollectedMonitoredItemNotifications, " publishingIntervalCount =", this.publishIntervalCount);
1071
+ }
1072
+ // give a chance to the publish engine to cancel timed out publish requests
1073
+ this.publishEngine._on_tick();
1074
+ this.publishIntervalCount += 1;
1075
+ if (this.state === SubscriptionState.LATE) {
1076
+ this.increaseLifeTimeCounter();
1077
+ }
1078
+ if (this.lifeTimeHasExpired) {
1079
+ /* istanbul ignore next */
1080
+ doDebug && debugLog(chalk.red.bold(`Subscription ${this.id} has expired !!!!! => Terminating`));
1081
+ /**
1082
+ * notify the subscription owner that the subscription has expired by exceeding its life time.
1083
+ * @event expired
1084
+ *
1085
+ */
1086
+ this.emit("expired");
1087
+ // notify new terminated status only when subscription has timeout.
1088
+ doDebug && debugLog("adding StatusChangeNotification notification message for BadTimeout subscription = ", this.id);
1089
+ this._addNotificationMessage(new node_opcua_service_subscription_2.StatusChangeNotification({ status: node_opcua_status_code_1.StatusCodes.BadTimeout }));
1090
+ // kill timer and delete monitored items and transfer pending notification messages
1091
+ this.terminate();
1092
+ return;
1093
+ }
1094
+ const publishEngine = this.publishEngine;
1095
+ // istanbul ignore next
1096
+ doDebug && debugLog("Subscription#_tick self._pending_notifications= ", this._pending_notifications.size);
1097
+ if (publishEngine.pendingPublishRequestCount === 0 &&
1098
+ (this.hasPendingNotifications || this.hasUncollectedMonitoredItemNotifications)) {
1099
+ // istanbul ignore next
1100
+ doDebug &&
1101
+ debugLog("subscription set to LATE hasPendingNotifications = ", this.hasPendingNotifications, " hasUncollectedMonitoredItemNotifications =", this.hasUncollectedMonitoredItemNotifications);
1102
+ this.state = SubscriptionState.LATE;
1103
+ return;
1104
+ }
1105
+ if (publishEngine.pendingPublishRequestCount > 0) {
1106
+ if (this.hasPendingNotifications) {
1107
+ // simply pop pending notification and send it
1108
+ this.process_subscription();
1109
+ }
1110
+ else if (this.hasUncollectedMonitoredItemNotifications) {
1111
+ this.process_subscription();
1112
+ }
1113
+ else {
1114
+ this._process_keepAlive();
1115
+ }
1116
+ }
1117
+ else {
1118
+ if (this.state !== SubscriptionState.LATE) {
1119
+ this._process_keepAlive();
1120
+ }
1121
+ else {
1122
+ this.resetKeepAliveCounter();
1123
+ }
1124
+ }
1125
+ }
1126
+ /**
1127
+ * @private
1128
+ */
1129
+ _sendKeepAliveResponse() {
1130
+ const future_sequence_number = this._get_future_sequence_number();
1131
+ if (this.publishEngine.send_keep_alive_response(this.id, future_sequence_number)) {
1132
+ this.messageSent = true;
1133
+ // istanbul ignore next
1134
+ doDebug &&
1135
+ debugLog(` -> Subscription#_sendKeepAliveResponse subscriptionId ${this.id} future_sequence_number ${future_sequence_number}`);
1136
+ /**
1137
+ * notify the subscription owner that a keepalive message has to be sent.
1138
+ * @event keepalive
1139
+ *
1140
+ */
1141
+ this.emit("keepalive", future_sequence_number);
1142
+ this.state = SubscriptionState.KEEPALIVE;
1143
+ return true;
1144
+ }
1145
+ return false;
1146
+ }
1147
+ /**
1148
+ * Reset the Lifetime Counter Variable to the value specified for the lifetime of a Subscription in
1149
+ * the CreateSubscription Service( 5.13.2).
1150
+ * @private
1151
+ */
1152
+ resetKeepAliveCounter() {
1153
+ this._keep_alive_counter = 0;
1154
+ // istanbul ignore next
1155
+ doDebug &&
1156
+ debugLog(" -> subscriptionId", this.id, " Resetting keepAliveCounter = ", this._keep_alive_counter, this.maxKeepAliveCount);
1157
+ }
1158
+ /**
1159
+ * @private
1160
+ */
1161
+ increaseKeepAliveCounter() {
1162
+ this._keep_alive_counter += 1;
1163
+ // istanbul ignore next
1164
+ doDebug &&
1165
+ debugLog(" -> subscriptionId", this.id, " Increasing keepAliveCounter = ", this._keep_alive_counter, this.maxKeepAliveCount);
1166
+ }
1167
+ /**
1168
+ * @private
1169
+ */
1170
+ _addNotificationMessage(notificationData, monitoredItemId) {
1171
+ // istanbul ignore next
1172
+ doDebug && debugLog(chalk.yellow("Subscription#_addNotificationMessage"), notificationData.toString());
1173
+ this._pending_notifications.push({
1174
+ monitoredItemId,
1175
+ notification: notificationData,
1176
+ publishTime: new Date(),
1177
+ start_tick: this.publishIntervalCount
1178
+ });
1179
+ }
1180
+ /**
1181
+ * @internal
1182
+ * @param monitoredItemId
1183
+ */
1184
+ _removePendingNotificationsFor(monitoredItemId) {
1185
+ const nbRemovedNotification = this._pending_notifications.filterOut((e) => e.monitoredItemId === monitoredItemId);
1186
+ doDebug && debugLog(`Removed ${nbRemovedNotification} notifications`);
1187
+ }
1188
+ /**
1189
+ * Extract the next Notification that is ready to be sent to the client.
1190
+ * @return the Notification to send._pending_notifications
1191
+ */
1192
+ _popNotificationToSend() {
1193
+ (0, node_opcua_assert_1.assert)(this._pending_notifications.size > 0);
1194
+ const notificationMessage = new node_opcua_service_subscription_2.NotificationMessage({
1195
+ sequenceNumber: 0xffffffff,
1196
+ notificationData: [],
1197
+ publishTime: new Date()
1198
+ }); //
1199
+ const dataChangeNotifications = new node_opcua_service_subscription_2.DataChangeNotification({
1200
+ monitoredItems: []
1201
+ });
1202
+ const eventNotificationList = new node_opcua_service_subscription_2.EventNotificationList({
1203
+ events: []
1204
+ });
1205
+ let statusChangeNotification;
1206
+ let i = 0;
1207
+ let hasEventFieldList = 0;
1208
+ let hasMonitoredItemNotification = 0;
1209
+ const m = this.maxNotificationsPerPublish;
1210
+ while (i < m && this._pending_notifications.size > 0) {
1211
+ if (hasEventFieldList || hasMonitoredItemNotification) {
1212
+ const notification1 = this._pending_notifications.first().notification;
1213
+ if (notification1 instanceof node_opcua_service_subscription_2.StatusChangeNotification) {
1214
+ break;
1215
+ }
1216
+ }
1217
+ const notification = this._pending_notifications.shift().notification;
1218
+ if (notification instanceof node_opcua_service_subscription_2.MonitoredItemNotification) {
1219
+ (0, node_opcua_assert_1.assert)(notification.clientHandle !== 4294967295);
1220
+ dataChangeNotifications.monitoredItems.push(notification);
1221
+ hasMonitoredItemNotification = 1;
1222
+ }
1223
+ else if (notification instanceof node_opcua_types_1.EventFieldList) {
1224
+ eventNotificationList.events.push(notification);
1225
+ hasEventFieldList = 1;
1226
+ }
1227
+ else if (notification instanceof node_opcua_service_subscription_2.StatusChangeNotification) {
1228
+ // to do
1229
+ statusChangeNotification = notification;
1230
+ break;
1231
+ }
1232
+ i += 1;
1233
+ }
1234
+ if (dataChangeNotifications.monitoredItems.length) {
1235
+ notificationMessage.notificationData.push(dataChangeNotifications);
1236
+ }
1237
+ if (eventNotificationList.events.length) {
1238
+ notificationMessage.notificationData.push(eventNotificationList);
1239
+ }
1240
+ if (statusChangeNotification) {
1241
+ notificationMessage.notificationData.push(statusChangeNotification);
1242
+ }
1243
+ return notificationMessage;
1244
+ }
1245
+ /**
1246
+ * discardOldSentNotification find all sent notification message that have expired keep-alive
1247
+ * and destroy them.
1248
+ * @private
1249
+ *
1250
+ * Subscriptions maintain a retransmission queue of sent NotificationMessages.
1251
+ * NotificationMessages are retained in this queue until they are acknowledged or until they have
1252
+ * been in the queue for a minimum of one keep-alive interval.
1253
+ *
1254
+ */
1255
+ discardOldSentNotifications() {
1256
+ // Sessions maintain a retransmission queue of sent NotificationMessages. NotificationMessages
1257
+ // are retained in this queue until they are acknowledged. The Session shall maintain a
1258
+ // retransmission queue size of at least two times the number of Publish requests per Session the
1259
+ // Server supports. Clients are required to acknowledge NotificationMessages as they are received. In the
1260
+ // case of a retransmission queue overflow, the oldest sent NotificationMessage gets deleted. If a
1261
+ // Subscription is transferred to another Session, the queued NotificationMessages for this
1262
+ // Subscription are moved from the old to the new Session.
1263
+ if (maxNotificationMessagesInQueue <= this._sent_notification_messages.length) {
1264
+ doDebug && debugLog("discardOldSentNotifications = ", this._sent_notification_messages.length);
1265
+ this._sent_notification_messages.splice(this._sent_notification_messages.length - maxNotificationMessagesInQueue);
1266
+ }
1267
+ }
1268
+ /**
1269
+ * @param timestampsToReturn
1270
+ * @param monitoredItemCreateRequest
1271
+ * @param node
1272
+ * @private
1273
+ */
1274
+ _createMonitoredItemStep2(timestampsToReturn, monitoredItemCreateRequest, node) {
1275
+ // note : most of the parameter inconsistencies shall have been handled by the caller
1276
+ // any error here will raise an assert here
1277
+ (0, node_opcua_assert_1.assert)(monitoredItemCreateRequest instanceof node_opcua_service_subscription_2.MonitoredItemCreateRequest);
1278
+ const itemToMonitor = monitoredItemCreateRequest.itemToMonitor;
1279
+ // xx check if attribute Id invalid (we only support Value or EventNotifier )
1280
+ // xx assert(itemToMonitor.attributeId !== AttributeIds.INVALID);
1281
+ this.monitoredItemIdCounter += 1;
1282
+ const monitoredItemId = getNextMonitoredItemId();
1283
+ const requestedParameters = monitoredItemCreateRequest.requestedParameters;
1284
+ // adjust requestedParameters.samplingInterval
1285
+ requestedParameters.samplingInterval = this.adjustSamplingInterval(requestedParameters.samplingInterval, node);
1286
+ // reincorporate monitoredItemId and itemToMonitor into the requestedParameters
1287
+ const options = requestedParameters;
1288
+ options.monitoredItemId = monitoredItemId;
1289
+ options.itemToMonitor = itemToMonitor;
1290
+ const monitoredItem = new monitored_item_1.MonitoredItem(options);
1291
+ monitoredItem.timestampsToReturn = timestampsToReturn;
1292
+ monitoredItem.$subscription = this;
1293
+ (0, node_opcua_assert_1.assert)(monitoredItem.monitoredItemId === monitoredItemId);
1294
+ this.monitoredItems[monitoredItemId] = monitoredItem;
1295
+ this.globalCounter.totalMonitoredItemCount += 1;
1296
+ (0, node_opcua_assert_1.assert)(monitoredItem.clientHandle !== 4294967295);
1297
+ const filterResult = _process_filter(node, requestedParameters.filter);
1298
+ const monitoredItemCreateResult = new node_opcua_service_subscription_2.MonitoredItemCreateResult({
1299
+ filterResult,
1300
+ monitoredItemId,
1301
+ revisedQueueSize: monitoredItem.queueSize,
1302
+ revisedSamplingInterval: monitoredItem.samplingInterval,
1303
+ statusCode: node_opcua_status_code_1.StatusCodes.Good
1304
+ });
1305
+ // this.emit("monitoredItem", monitoredItem, itemToMonitor);
1306
+ return monitoredItemCreateResult;
1307
+ }
1308
+ /**
1309
+ *
1310
+ * @param monitoredItem
1311
+ * @param monitoredItemCreateRequest
1312
+ * @private
1313
+ */
1314
+ _createMonitoredItemStep3(monitoredItem, monitoredItemCreateRequest) {
1315
+ if (!monitoredItem) {
1316
+ return;
1317
+ }
1318
+ (0, node_opcua_assert_1.assert)(monitoredItem.monitoringMode === node_opcua_service_subscription_2.MonitoringMode.Invalid);
1319
+ (0, node_opcua_assert_1.assert)(typeof monitoredItem.samplingFunc === "function", " expecting a sampling function here");
1320
+ const monitoringMode = monitoredItemCreateRequest.monitoringMode; // Disabled, Sampling, Reporting
1321
+ monitoredItem.setMonitoringMode(monitoringMode);
1322
+ }
1323
+ _harvestMonitoredItems() {
1324
+ for (const monitoredItem of Object.values(this.monitoredItems)) {
1325
+ const notifications_chunks = monitoredItem.extractMonitoredItemNotifications();
1326
+ for (const chunk of notifications_chunks) {
1327
+ this._addNotificationMessage(chunk, monitoredItem.monitoredItemId);
1328
+ }
1329
+ }
1330
+ this._hasUncollectedMonitoredItemNotifications = false;
1331
+ }
1332
+ }
1333
+ exports.Subscription = Subscription;
1334
+ Subscription.minimumPublishingInterval = 50; // fastest possible
1335
+ Subscription.defaultPublishingInterval = 1000; // one second
1336
+ Subscription.maximumPublishingInterval = 1000 * 60; // one minute
1337
+ Subscription.maxNotificationPerPublishHighLimit = 1000;
1338
+ Subscription.minimumLifetimeDuration = 5 * 1000; // // we want 2 seconds minimum lifetime for any subscription
1339
+ Subscription.maximumLifetimeDuration = 60 * 60 * 1000; // 1 hour
1340
+ /**
1341
+ * maximum number of monitored item in a subscription to be used
1342
+ * when serverCapacity.maxMonitoredItems and serverCapacity.maxMonitoredItemsPerSubscription are not set.
1343
+ */
1344
+ Subscription.defaultMaxMonitoredItemCount = 20000;
1345
+ Subscription.registry = new node_opcua_object_registry_1.ObjectRegistry();
1346
+ (0, node_opcua_assert_1.assert)(Subscription.maximumPublishingInterval < 2147483647, "maximumPublishingInterval cannot exceed (2**31-1) ms ");
1347
1347
  //# sourceMappingURL=server_subscription.js.map