node-opcua-server 2.72.1 → 2.73.1

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 (41) hide show
  1. package/dist/helper.d.ts +10 -0
  2. package/dist/helper.js +76 -0
  3. package/dist/helper.js.map +1 -0
  4. package/dist/index.d.ts +1 -0
  5. package/dist/index.js +1 -0
  6. package/dist/index.js.map +1 -1
  7. package/dist/monitored_item.js +2 -1
  8. package/dist/monitored_item.js.map +1 -1
  9. package/dist/opcua_server.js +5 -3
  10. package/dist/opcua_server.js.map +1 -1
  11. package/dist/server_capabilities.js +5 -4
  12. package/dist/server_capabilities.js.map +1 -1
  13. package/dist/server_engine.d.ts +1 -1
  14. package/dist/server_engine.js +25 -25
  15. package/dist/server_engine.js.map +1 -1
  16. package/dist/server_publish_engine.d.ts +1 -1
  17. package/dist/server_publish_engine.js +11 -6
  18. package/dist/server_publish_engine.js.map +1 -1
  19. package/dist/server_publish_engine_for_orphan_subscriptions.js +3 -1
  20. package/dist/server_publish_engine_for_orphan_subscriptions.js.map +1 -1
  21. package/dist/server_session.d.ts +4 -3
  22. package/dist/server_session.js +7 -6
  23. package/dist/server_session.js.map +1 -1
  24. package/dist/server_subscription.d.ts +8 -2
  25. package/dist/server_subscription.js +70 -59
  26. package/dist/server_subscription.js.map +1 -1
  27. package/dist/sessions_compatible_for_transfer.d.ts +1 -1
  28. package/dist/sessions_compatible_for_transfer.js +3 -0
  29. package/dist/sessions_compatible_for_transfer.js.map +1 -1
  30. package/package.json +38 -37
  31. package/source/helper.ts +87 -0
  32. package/source/index.ts +2 -1
  33. package/source/monitored_item.ts +3 -2
  34. package/source/opcua_server.ts +5 -3
  35. package/source/server_capabilities.ts +5 -4
  36. package/source/server_engine.ts +29 -27
  37. package/source/server_publish_engine.ts +15 -7
  38. package/source/server_publish_engine_for_orphan_subscriptions.ts +6 -1
  39. package/source/server_session.ts +15 -13
  40. package/source/server_subscription.ts +83 -69
  41. package/source/sessions_compatible_for_transfer.ts +5 -1
@@ -91,9 +91,11 @@ function _adjust_lifeTimeCount(lifeTimeCount: number, maxKeepAliveCount: number,
91
91
  // "The lifetime count shall be a minimum of three times the keep keep-alive count."
92
92
  lifeTimeCount = Math.max(lifeTimeCount, maxKeepAliveCount * 3);
93
93
 
94
- const minTicks = Math.ceil((5 * 1000) / publishingInterval); // we want 5 seconds min
94
+ const minTicks = Math.ceil(Subscription.minimumLifetimeDuration / publishingInterval);
95
+ const maxTicks = Math.floor(Subscription.maximumLifetimeDuration / publishingInterval);
95
96
 
96
97
  lifeTimeCount = Math.max(minTicks, lifeTimeCount);
98
+ lifeTimeCount = Math.min(maxTicks, lifeTimeCount);
97
99
  return lifeTimeCount;
98
100
  }
99
101
 
@@ -186,7 +188,7 @@ function createSubscriptionDiagnostics(subscription: Subscription): Subscription
186
188
 
187
189
  const subscriptionDiagnostics = new SubscriptionDiagnosticsDataType({});
188
190
 
189
- const subscription_subscriptionDiagnostics = subscriptionDiagnostics as any;
191
+ const subscription_subscriptionDiagnostics = subscriptionDiagnostics as SubscriptionDiagnosticsDataTypePriv as any;
190
192
  subscription_subscriptionDiagnostics.$subscription = subscription;
191
193
  // "sessionId"
192
194
  subscription_subscriptionDiagnostics.__defineGetter__(
@@ -300,13 +302,29 @@ function createSubscriptionDiagnostics(subscription: Subscription): Subscription
300
302
  "transferredToAltClientCount",
301
303
  "transferredToSameClientCount",
302
304
  "latePublishRequestCount",
303
- "currentKeepAliveCount",
304
- "currentLifetimeCount",
305
305
  "unacknowledgedMessageCount",
306
306
  "discardedMessageCount",
307
307
  "monitoringQueueOverflowCount",
308
308
  "eventQueueOverFlowCount"
309
309
  */
310
+ subscription_subscriptionDiagnostics.__defineGetter__(
311
+ "currentKeepAliveCount",
312
+ function (this: SubscriptionDiagnosticsDataTypePriv): number {
313
+ if (!this.$subscription) {
314
+ return 0;
315
+ }
316
+ return this.$subscription.currentKeepAliveCount;
317
+ }
318
+ );
319
+ subscription_subscriptionDiagnostics.__defineGetter__(
320
+ "currentLifetimeCount",
321
+ function (this: SubscriptionDiagnosticsDataTypePriv): number {
322
+ if (!this.$subscription) {
323
+ return 0;
324
+ }
325
+ return this.$subscription.currentLifetimeCount;
326
+ }
327
+ );
310
328
  // add object in Variable SubscriptionDiagnosticArray (i=2290) ( Array of SubscriptionDiagnostics)
311
329
  // add properties in Variable to reflect
312
330
  return subscriptionDiagnostics as SubscriptionDiagnosticsDataTypePriv;
@@ -453,15 +471,17 @@ export interface ServerCapabilitiesPartial {
453
471
  export class Subscription extends EventEmitter {
454
472
  public static minimumPublishingInterval = 50; // fastest possible
455
473
  public static defaultPublishingInterval = 1000; // one second
456
- public static maximumPublishingInterval: number = 1000 * 60 * 60 * 24 * 15; // 15 days
474
+ public static maximumPublishingInterval: number = 1000 * 60; // one minute
457
475
  public static maxNotificationPerPublishHighLimit = 1000;
476
+ public static minimumLifetimeDuration = 5 * 1000; // // we want 2 seconds minimum lifetime for any subscription
477
+ public static maximumLifetimeDuration = 60 * 60 * 1000; // 1 hour
458
478
 
459
479
  /**
460
480
  * maximum number of monitored item in a subscription to be used
461
481
  * when serverCapacity.maxMonitoredItems and serverCapacity.maxMonitoredItemsPerSubscription are not set.
462
482
  */
463
483
  public static defaultMaxMonitoredItemCount = 20000;
464
-
484
+
465
485
  /**
466
486
  * @deprecated use serverCapacity.maxMonitoredItems and serverCapacity.maxMonitoredItemsPerSubscription instead
467
487
  */
@@ -471,7 +491,6 @@ export class Subscription extends EventEmitter {
471
491
 
472
492
  public static registry = new ObjectRegistry();
473
493
 
474
- public sessionId: NodeId;
475
494
  public publishEngine?: IServerSidePublishEngine;
476
495
  public id: number;
477
496
  public priority: number;
@@ -518,10 +537,31 @@ export class Subscription extends EventEmitter {
518
537
  */
519
538
  public monitoredItemIdCounter: number;
520
539
 
521
- public state: SubscriptionState;
540
+ private _state: SubscriptionState = -1 as SubscriptionState;
541
+ public set state(value: SubscriptionState) {
542
+ if (this._state !== value) {
543
+ this._state = value;
544
+ this.emit("stateChanged", value);
545
+ }
546
+ }
547
+ public get state(): SubscriptionState {
548
+ return this._state;
549
+ }
550
+
522
551
  public messageSent: boolean;
523
552
  public $session?: ServerSession;
524
553
 
554
+ public get sessionId(): NodeId {
555
+ return this.$session ? this.$session.nodeId : NodeId.nullNodeId;
556
+ }
557
+
558
+ public get currentLifetimeCount(): number {
559
+ return this._life_time_counter;
560
+ }
561
+ public get currentKeepAliveCount(): number {
562
+ return this._keep_alive_counter;
563
+ }
564
+
525
565
  private _life_time_counter: number;
526
566
  private _keep_alive_counter = 0;
527
567
  private _pending_notifications: Queue<InternalNotification>;
@@ -541,7 +581,6 @@ export class Subscription extends EventEmitter {
541
581
 
542
582
  Subscription.registry.register(this);
543
583
 
544
- this.sessionId = options.sessionId || NodeId.nullNodeId;
545
584
  assert(this.sessionId instanceof NodeId, "expecting a sessionId NodeId");
546
585
 
547
586
  this.publishEngine = options.publishEngine!;
@@ -595,7 +634,8 @@ export class Subscription extends EventEmitter {
595
634
  debugLog(chalk.green(`creating subscription ${this.id}`));
596
635
 
597
636
  this.serverCapabilities = options.serverCapabilities;
598
- this.serverCapabilities.maxMonitoredItems = this.serverCapabilities.maxMonitoredItems || Subscription.defaultMaxMonitoredItemCount;
637
+ this.serverCapabilities.maxMonitoredItems =
638
+ this.serverCapabilities.maxMonitoredItems || Subscription.defaultMaxMonitoredItemCount;
599
639
  this.serverCapabilities.maxMonitoredItemsPerSubscription =
600
640
  this.serverCapabilities.maxMonitoredItemsPerSubscription || Subscription.defaultMaxMonitoredItemCount;
601
641
  this.globalCounter = options.globalCounter;
@@ -674,7 +714,7 @@ export class Subscription extends EventEmitter {
674
714
  * @private
675
715
  */
676
716
  public get keepAliveCounterHasExpired(): boolean {
677
- return this._keep_alive_counter >= this.maxKeepAliveCount;
717
+ return this._keep_alive_counter >= this.maxKeepAliveCount || this.state === SubscriptionState.LATE;
678
718
  }
679
719
 
680
720
  /**
@@ -691,6 +731,10 @@ export class Subscription extends EventEmitter {
691
731
  */
692
732
  public increaseLifeTimeCounter(): void {
693
733
  this._life_time_counter += 1;
734
+ if (this._life_time_counter >= this.lifeTimeCount) {
735
+ this.emit("lifeTimeExpired");
736
+ }
737
+ this.emit("lifeTimeCounterChanged", this._life_time_counter);
694
738
  }
695
739
 
696
740
  /**
@@ -823,8 +867,6 @@ export class Subscription extends EventEmitter {
823
867
  this._pending_notifications.clear();
824
868
  this._sent_notification_messages = [];
825
869
 
826
- this.sessionId = new NodeId();
827
-
828
870
  this.$session = undefined;
829
871
  this.removeAllListeners();
830
872
 
@@ -1401,7 +1443,9 @@ export class Subscription extends EventEmitter {
1401
1443
  " -> subscription.state === LATE , " +
1402
1444
  "because keepAlive Response cannot be send due to lack of PublishRequest"
1403
1445
  );
1404
- this.state = SubscriptionState.LATE;
1446
+ if (this.messageSent || this.keepAliveCounterHasExpired) {
1447
+ this.state = SubscriptionState.LATE;
1448
+ }
1405
1449
  }
1406
1450
  }
1407
1451
  }
@@ -1433,7 +1477,9 @@ export class Subscription extends EventEmitter {
1433
1477
 
1434
1478
  // make sure that a keep-alive Message will be send at the end of the first publishing cycle
1435
1479
  // if there are no Notifications ready.
1436
- this._keep_alive_counter = this.maxKeepAliveCount;
1480
+ this._keep_alive_counter = 0; // this.maxKeepAliveCount;
1481
+ assert(this.messageSent === false);
1482
+ assert(this.state === SubscriptionState.CREATING);
1437
1483
 
1438
1484
  assert(this.publishingInterval >= Subscription.minimumPublishingInterval);
1439
1485
  this.timerId = setInterval(this._tick.bind(this), this.publishingInterval);
@@ -1452,14 +1498,6 @@ export class Subscription extends EventEmitter {
1452
1498
  if (doDebug) {
1453
1499
  debugLog(`Subscription#_tick id ${this.id} aborted=${this.aborted} state=${SubscriptionState[this.state]}`);
1454
1500
  }
1455
-
1456
- if (this.aborted) {
1457
- // xx console.log(" Log aborted")
1458
- // xx // underlying channel has been aborted ...
1459
- // xx self.publishEngine.cancelPendingPublishRequestBeforeChannelChange();
1460
- // xx // let's still increase lifetime counter to detect timeout
1461
- }
1462
-
1463
1501
  if (this.state === SubscriptionState.CLOSED) {
1464
1502
  warningLog(`Warning: Subscription#_tick id ${this.id} called while subscription is CLOSED`);
1465
1503
  return;
@@ -1480,17 +1518,19 @@ export class Subscription extends EventEmitter {
1480
1518
  );
1481
1519
  }
1482
1520
 
1521
+ // give a chance to the publish engine to cancel timed out publish requests
1483
1522
  this.publishEngine!._on_tick();
1484
1523
 
1485
1524
  this.publishIntervalCount += 1;
1486
1525
 
1487
- this.increaseLifeTimeCounter();
1526
+ if (this.state === SubscriptionState.LATE) {
1527
+ this.increaseLifeTimeCounter();
1528
+ }
1488
1529
 
1489
1530
  if (this.lifeTimeHasExpired) {
1490
1531
  /* istanbul ignore next */
1491
- if (doDebug) {
1492
- debugLog(chalk.red.bold(`Subscription ${this.id} has expired !!!!! => Terminating`));
1493
- }
1532
+ doDebug && debugLog(chalk.red.bold(`Subscription ${this.id} has expired !!!!! => Terminating`));
1533
+
1494
1534
  /**
1495
1535
  * notify the subscription owner that the subscription has expired by exceeding its life time.
1496
1536
  * @event expired
@@ -1499,7 +1539,7 @@ export class Subscription extends EventEmitter {
1499
1539
  this.emit("expired");
1500
1540
 
1501
1541
  // notify new terminated status only when subscription has timeout.
1502
- debugLog("adding StatusChangeNotification notification message for BadTimeout subscription = ", this.id);
1542
+ doDebug && debugLog("adding StatusChangeNotification notification message for BadTimeout subscription = ", this.id);
1503
1543
  this._addNotificationMessage(new StatusChangeNotification({ status: StatusCodes.BadTimeout }));
1504
1544
 
1505
1545
  // kill timer and delete monitored items and transfer pending notification messages
@@ -1511,23 +1551,21 @@ export class Subscription extends EventEmitter {
1511
1551
  const publishEngine = this.publishEngine!;
1512
1552
 
1513
1553
  // istanbul ignore next
1514
- if (doDebug) {
1515
- debugLog("Subscription#_tick self._pending_notifications= ", this._pending_notifications.size);
1516
- }
1554
+ doDebug && debugLog("Subscription#_tick self._pending_notifications= ", this._pending_notifications.size);
1517
1555
 
1518
1556
  if (
1519
1557
  publishEngine.pendingPublishRequestCount === 0 &&
1520
1558
  (this.hasPendingNotifications || this.hasUncollectedMonitoredItemNotifications)
1521
1559
  ) {
1522
1560
  // istanbul ignore next
1523
- if (doDebug) {
1561
+ doDebug &&
1524
1562
  debugLog(
1525
1563
  "subscription set to LATE hasPendingNotifications = ",
1526
1564
  this.hasPendingNotifications,
1527
1565
  " hasUncollectedMonitoredItemNotifications =",
1528
1566
  this.hasUncollectedMonitoredItemNotifications
1529
1567
  );
1530
- }
1568
+
1531
1569
  this.state = SubscriptionState.LATE;
1532
1570
  return;
1533
1571
  }
@@ -1542,7 +1580,11 @@ export class Subscription extends EventEmitter {
1542
1580
  this._process_keepAlive();
1543
1581
  }
1544
1582
  } else {
1545
- this._process_keepAlive();
1583
+ if (this.state !== SubscriptionState.LATE) {
1584
+ this._process_keepAlive();
1585
+ } else {
1586
+ this.resetKeepAliveCounter();
1587
+ }
1546
1588
  }
1547
1589
  }
1548
1590
 
@@ -1555,11 +1597,10 @@ export class Subscription extends EventEmitter {
1555
1597
  if (this.publishEngine!.send_keep_alive_response(this.id, future_sequence_number)) {
1556
1598
  this.messageSent = true;
1557
1599
  // istanbul ignore next
1558
- if (doDebug) {
1600
+ doDebug &&
1559
1601
  debugLog(
1560
1602
  ` -> Subscription#_sendKeepAliveResponse subscriptionId ${this.id} future_sequence_number ${future_sequence_number}`
1561
1603
  );
1562
- }
1563
1604
  /**
1564
1605
  * notify the subscription owner that a keepalive message has to be sent.
1565
1606
  * @event keepalive
@@ -1582,7 +1623,7 @@ export class Subscription extends EventEmitter {
1582
1623
  this._keep_alive_counter = 0;
1583
1624
 
1584
1625
  // istanbul ignore next
1585
- if (doDebug) {
1626
+ doDebug &&
1586
1627
  debugLog(
1587
1628
  " -> subscriptionId",
1588
1629
  this.id,
@@ -1590,7 +1631,6 @@ export class Subscription extends EventEmitter {
1590
1631
  this._keep_alive_counter,
1591
1632
  this.maxKeepAliveCount
1592
1633
  );
1593
- }
1594
1634
  }
1595
1635
 
1596
1636
  /**
@@ -1600,7 +1640,7 @@ export class Subscription extends EventEmitter {
1600
1640
  this._keep_alive_counter += 1;
1601
1641
 
1602
1642
  // istanbul ignore next
1603
- if (doDebug) {
1643
+ doDebug &&
1604
1644
  debugLog(
1605
1645
  " -> subscriptionId",
1606
1646
  this.id,
@@ -1608,7 +1648,6 @@ export class Subscription extends EventEmitter {
1608
1648
  this._keep_alive_counter,
1609
1649
  this.maxKeepAliveCount
1610
1650
  );
1611
- }
1612
1651
  }
1613
1652
 
1614
1653
  /**
@@ -1616,9 +1655,8 @@ export class Subscription extends EventEmitter {
1616
1655
  */
1617
1656
  private _addNotificationMessage(notificationData: QueueItem | StatusChangeNotification, monitoredItemId?: number) {
1618
1657
  // istanbul ignore next
1619
- if (doDebug) {
1620
- debugLog(chalk.yellow("Subscription#_addNotificationMessage"), notificationData.toString());
1621
- }
1658
+ doDebug && debugLog(chalk.yellow("Subscription#_addNotificationMessage"), notificationData.toString());
1659
+
1622
1660
  this._pending_notifications.push({
1623
1661
  monitoredItemId,
1624
1662
  notification: notificationData,
@@ -1633,9 +1671,7 @@ export class Subscription extends EventEmitter {
1633
1671
  */
1634
1672
  private _removePendingNotificationsFor(monitoredItemId: number) {
1635
1673
  const nbRemovedNotification = this._pending_notifications.filterOut((e) => e.monitoredItemId === monitoredItemId);
1636
- if (doDebug) {
1637
- debugLog(`Removed ${nbRemovedNotification} notifications`);
1638
- }
1674
+ doDebug && debugLog(`Removed ${nbRemovedNotification} notifications`);
1639
1675
  }
1640
1676
  /**
1641
1677
  * Extract the next Notification that is ready to be sent to the client.
@@ -1717,7 +1753,7 @@ export class Subscription extends EventEmitter {
1717
1753
  // Subscription is transferred to another Session, the queued NotificationMessages for this
1718
1754
  // Subscription are moved from the old to the new Session.
1719
1755
  if (maxNotificationMessagesInQueue <= this._sent_notification_messages.length) {
1720
- debugLog("discardOldSentNotifications = ", this._sent_notification_messages.length);
1756
+ doDebug && debugLog("discardOldSentNotifications = ", this._sent_notification_messages.length);
1721
1757
  this._sent_notification_messages.splice(this._sent_notification_messages.length - maxNotificationMessagesInQueue);
1722
1758
  }
1723
1759
  }
@@ -1812,26 +1848,4 @@ export class Subscription extends EventEmitter {
1812
1848
  }
1813
1849
  }
1814
1850
 
1815
- /**
1816
- * extract up to maxNotificationsPerPublish notifications
1817
- * @param the full array of monitored items
1818
- * @param maxNotificationsPerPublish the maximum number of notification to extract
1819
- * @return an extract of array of monitored item matching at most maxNotificationsPerPublish
1820
- * @private
1821
- */
1822
- function extract_notifications_chunk<T>(monitoredItems: Queue<T>, maxNotificationsPerPublish: number): T[] {
1823
- let n = maxNotificationsPerPublish === 0 ? monitoredItems.size : Math.min(monitoredItems.size, maxNotificationsPerPublish);
1824
-
1825
- const chunk_monitoredItems: T[] = [];
1826
- while (n) {
1827
- chunk_monitoredItems.push(monitoredItems.shift()!);
1828
- n--;
1829
- }
1830
- return chunk_monitoredItems;
1831
- }
1832
-
1833
- function filter_instanceof(Class: any, e: any): boolean {
1834
- return e instanceof Class;
1835
- }
1836
-
1837
1851
  assert(Subscription.maximumPublishingInterval < 2147483647, "maximumPublishingInterval cannot exceed (2**31-1) ms ");
@@ -2,7 +2,11 @@ import { assert } from "node-opcua-assert";
2
2
  import { UserIdentityToken, AnonymousIdentityToken, UserNameIdentityToken, X509IdentityToken } from "node-opcua-types";
3
3
  import { ServerSession } from "./server_session";
4
4
 
5
- export function sessionsCompatibleForTransfer(sessionSrc: ServerSession, sessionDest: ServerSession): boolean {
5
+ export function sessionsCompatibleForTransfer(sessionSrc: ServerSession | undefined, sessionDest: ServerSession): boolean {
6
+
7
+ if (!sessionSrc) {
8
+ return true;
9
+ }
6
10
  assert(sessionDest);
7
11
  assert(sessionSrc);
8
12
  if (!sessionSrc.userIdentityToken && !sessionDest.userIdentityToken) {