node-opcua-server 2.119.2 → 2.121.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/LICENSE +1 -1
  2. package/dist/addressSpace_accessor.js +1 -3
  3. package/dist/addressSpace_accessor.js.map +1 -1
  4. package/dist/base_server.js.map +1 -1
  5. package/dist/factory.js.map +1 -1
  6. package/dist/helper.js.map +1 -1
  7. package/dist/history_server_capabilities.js.map +1 -1
  8. package/dist/monitored_item.d.ts +6 -6
  9. package/dist/monitored_item.js +55 -38
  10. package/dist/monitored_item.js.map +1 -1
  11. package/dist/node_sampler.js.map +1 -1
  12. package/dist/opcua_server.d.ts +10 -4
  13. package/dist/opcua_server.js +18 -20
  14. package/dist/opcua_server.js.map +1 -1
  15. package/dist/queue.js.map +1 -1
  16. package/dist/register_server_manager.js +1 -1
  17. package/dist/register_server_manager.js.map +1 -1
  18. package/dist/register_server_manager_mdns_only.js +1 -1
  19. package/dist/register_server_manager_mdns_only.js.map +1 -1
  20. package/dist/server_capabilities.js +2 -2
  21. package/dist/server_capabilities.js.map +1 -1
  22. package/dist/server_end_point.d.ts +6 -0
  23. package/dist/server_end_point.js +16 -10
  24. package/dist/server_end_point.js.map +1 -1
  25. package/dist/server_engine.d.ts +3 -3
  26. package/dist/server_engine.js +3 -3
  27. package/dist/server_engine.js.map +1 -1
  28. package/dist/server_publish_engine.d.ts +1 -4
  29. package/dist/server_publish_engine.js +95 -91
  30. package/dist/server_publish_engine.js.map +1 -1
  31. package/dist/server_publish_engine_for_orphan_subscriptions.js +1 -1
  32. package/dist/server_publish_engine_for_orphan_subscriptions.js.map +1 -1
  33. package/dist/server_session.js +3 -1
  34. package/dist/server_session.js.map +1 -1
  35. package/dist/server_subscription.d.ts +22 -14
  36. package/dist/server_subscription.js +46 -21
  37. package/dist/server_subscription.js.map +1 -1
  38. package/dist/sessions_compatible_for_transfer.js.map +1 -1
  39. package/dist/user_manager.js.map +1 -1
  40. package/dist/user_manager_ua.js.map +1 -1
  41. package/dist/validate_filter.js.map +1 -1
  42. package/package.json +48 -47
  43. package/source/addressSpace_accessor.ts +2 -3
  44. package/source/monitored_item.ts +68 -56
  45. package/source/opcua_server.ts +39 -24
  46. package/source/server_end_point.ts +27 -8
  47. package/source/server_engine.ts +6 -5
  48. package/source/server_publish_engine.ts +14 -12
  49. package/source/server_publish_engine_for_orphan_subscriptions.ts +1 -1
  50. package/source/server_session.ts +2 -2
  51. package/source/server_subscription.ts +84 -34
@@ -161,6 +161,8 @@ export class ServerSidePublishEngine extends EventEmitter implements IServerSide
161
161
  assert(destPublishEngine.getSubscriptionById(subscription.id));
162
162
  assert(!srcPublishEngine.getSubscriptionById(subscription.id));
163
163
 
164
+
165
+
164
166
  return subscription;
165
167
  }
166
168
 
@@ -313,8 +315,8 @@ export class ServerSidePublishEngine extends EventEmitter implements IServerSide
313
315
 
314
316
  public get currentMonitoredItemCount(): number {
315
317
  const subscriptions = Object.values(this._subscriptions);
316
- const result = subscriptions.reduce((cumul: number, subscription: Subscription) => {
317
- return cumul + subscription.monitoredItemCount;
318
+ const result = subscriptions.reduce((sum: number, subscription: Subscription) => {
319
+ return sum + subscription.monitoredItemCount;
318
320
  }, 0);
319
321
  assert(isFinite(result));
320
322
  return result;
@@ -342,7 +344,7 @@ export class ServerSidePublishEngine extends EventEmitter implements IServerSide
342
344
 
343
345
  delete this._subscriptions[subscription.id];
344
346
 
345
- while (this._feed_closed_subscription()) {
347
+ while (this.#_feed_closed_subscription()) {
346
348
  /* keep looping */
347
349
  }
348
350
  if (this.subscriptionCount === 0 && this._closed_subscriptions.length === 0) {
@@ -432,7 +434,7 @@ export class ServerSidePublishEngine extends EventEmitter implements IServerSide
432
434
  // add the publish request to the queue for later processing
433
435
  this._publish_request_queue.push(publishData);
434
436
 
435
- const processed = this._feed_closed_subscription();
437
+ const processed = this.#_feed_closed_subscription();
436
438
  //xx ( may be subscription has expired by themselves) assert(verif === this._publish_request_queue.length);
437
439
  //xx ( may be subscription has expired by themselves) assert(processed);
438
440
  return;
@@ -446,15 +448,15 @@ export class ServerSidePublishEngine extends EventEmitter implements IServerSide
446
448
 
447
449
  debugLog(chalk.bgWhite.red("Adding a PublishRequest to the queue "), this._publish_request_queue.length);
448
450
 
449
- this._feed_closed_subscription();
451
+ this.#_feed_closed_subscription();
450
452
 
451
- this._feed_late_subscription();
453
+ this.#_feed_late_subscription();
452
454
 
453
- this._handle_too_many_requests();
455
+ this.#_handle_too_many_requests();
454
456
  }
455
457
  }
456
458
 
457
- private _find_starving_subscription(): Subscription | null {
459
+ #_find_starving_subscription(): Subscription | null {
458
460
  const late_subscriptions = this.findLateSubscriptions();
459
461
  function compare_subscriptions(s1: Subscription, s2: Subscription): number {
460
462
  if (s1.priority === s2.priority) {
@@ -503,12 +505,12 @@ export class ServerSidePublishEngine extends EventEmitter implements IServerSide
503
505
  return starving_subscription;
504
506
  }
505
507
 
506
- private _feed_late_subscription() {
508
+ #_feed_late_subscription() {
507
509
  setImmediate(() => {
508
510
  if (!this.pendingPublishRequestCount) {
509
511
  return;
510
512
  }
511
- const starving_subscription = this._find_starving_subscription();
513
+ const starving_subscription = this.#_find_starving_subscription();
512
514
  if (starving_subscription) {
513
515
  doDebug &&
514
516
  debugLog(chalk.bgWhite.red("feeding most late subscription subscriptionId = "), starving_subscription.id);
@@ -517,7 +519,7 @@ export class ServerSidePublishEngine extends EventEmitter implements IServerSide
517
519
  });
518
520
  }
519
521
 
520
- private _feed_closed_subscription() {
522
+ #_feed_closed_subscription() {
521
523
  if (!this.pendingPublishRequestCount) {
522
524
  return false;
523
525
  }
@@ -564,7 +566,7 @@ export class ServerSidePublishEngine extends EventEmitter implements IServerSide
564
566
  this._publish_request_queue = [];
565
567
  }
566
568
 
567
- private _handle_too_many_requests() {
569
+ #_handle_too_many_requests() {
568
570
  if (this.pendingPublishRequestCount > this.maxPublishRequestInQueue) {
569
571
  traceLog(
570
572
  "server has received too many PublishRequest",
@@ -30,7 +30,7 @@ export class ServerSidePublishEngineForOrphanSubscription extends ServerSidePubl
30
30
  public add_subscription(subscription: Subscription): Subscription {
31
31
  debugLog(chalk.bgCyan.yellow.bold(" adding live subscription with id="), subscription.id, " to orphan");
32
32
 
33
- // detach subscription from old seession
33
+ // detach subscription from old session
34
34
  subscription.$session = undefined;
35
35
 
36
36
  super.add_subscription(subscription);
@@ -52,7 +52,6 @@ const theWatchDog = new WatchDog();
52
52
 
53
53
  const registeredNodeNameSpace = 9999;
54
54
 
55
-
56
55
  function on_channel_abort(this: ServerSession) {
57
56
  debugLog("ON CHANNEL ABORT ON SESSION!!!");
58
57
  /**
@@ -375,6 +374,7 @@ export class ServerSession extends EventEmitter implements ISubscriber, ISession
375
374
  * @return {Subscription}
376
375
  */
377
376
  public getSubscription(subscriptionId: number): Subscription | null {
377
+ if (!this.publishEngine) return null;
378
378
  const subscription = this.publishEngine.getSubscriptionById(subscriptionId);
379
379
  if (subscription && subscription.state === SubscriptionState.CLOSED) {
380
380
  // subscription is CLOSED but has not been notified yet
@@ -942,7 +942,7 @@ export class ServerSession extends EventEmitter implements ISubscriber, ISession
942
942
  }
943
943
 
944
944
  private _deleteSubscriptions() {
945
- if(!this.publishEngine) return;
945
+ if (!this.publishEngine) return;
946
946
  const subscriptions = this.publishEngine.subscriptions;
947
947
  for (const subscription of subscriptions) {
948
948
  this.deleteSubscription(subscription.id);
@@ -6,14 +6,23 @@
6
6
  import { EventEmitter } from "events";
7
7
  import chalk from "chalk";
8
8
 
9
- import { SessionContext, AddressSpace, BaseNode, Duration, UAObjectType } from "node-opcua-address-space";
9
+ import {
10
+ SessionContext,
11
+ AddressSpace,
12
+ BaseNode,
13
+ Duration,
14
+ UAObjectType,
15
+ ISessionContext,
16
+ IAddressSpace
17
+ } from "node-opcua-address-space";
10
18
  import { assert } from "node-opcua-assert";
11
19
  import { Byte, UInt32 } from "node-opcua-basic-types";
12
20
  import { SubscriptionDiagnosticsDataType } from "node-opcua-common";
13
- import { NodeClass, AttributeIds, isValidDataEncoding } from "node-opcua-data-model";
14
- import { TimestampsToReturn } from "node-opcua-data-value";
21
+ import { NodeClass, AttributeIds, isValidDataEncoding, QualifiedNameLike } from "node-opcua-data-model";
22
+ import { DataValue, TimestampsToReturn } from "node-opcua-data-value";
15
23
  import { checkDebugFlag, make_debugLog, make_warningLog } from "node-opcua-debug";
16
24
  import { NodeId } from "node-opcua-nodeid";
25
+ import { NumericRange } from "node-opcua-numeric-range";
17
26
  import { ObjectRegistry } from "node-opcua-object-registry";
18
27
  import { SequenceNumberGenerator } from "node-opcua-secure-channel";
19
28
  import { EventFilter, checkSelectClauses } from "node-opcua-service-filter";
@@ -81,7 +90,7 @@ function _adjust_maxKeepAliveCount(maxKeepAliveCount?: number /*,publishingInter
81
90
  return maxKeepAliveCount;
82
91
  }
83
92
 
84
- const MaxUint32 = 0xFFFFFFFF;
93
+ const MaxUint32 = 0xffffffff;
85
94
 
86
95
  function _adjust_lifeTimeCount(lifeTimeCount: number, maxKeepAliveCount: number, publishingInterval: number): number {
87
96
  lifeTimeCount = lifeTimeCount || 1;
@@ -137,7 +146,7 @@ function _getSequenceNumbers(arr: NotificationMessage[]): number[] {
137
146
  return arr.map((notificationMessage) => notificationMessage.sequenceNumber);
138
147
  }
139
148
 
140
- function analyseEventFilterResult(node: BaseNode, eventFilter: EventFilter): EventFilterResult {
149
+ function analyzeEventFilterResult(node: BaseNode, eventFilter: EventFilter): EventFilterResult {
141
150
  /* istanbul ignore next */
142
151
  if (!(eventFilter instanceof EventFilter)) {
143
152
  throw new Error("Internal Error");
@@ -154,13 +163,13 @@ function analyseEventFilterResult(node: BaseNode, eventFilter: EventFilter): Eve
154
163
  });
155
164
  }
156
165
 
157
- function analyseDataChangeFilterResult(node: BaseNode, dataChangeFilter: DataChangeFilter): null {
166
+ function analyzeDataChangeFilterResult(node: BaseNode, dataChangeFilter: DataChangeFilter): null {
158
167
  assert(dataChangeFilter instanceof DataChangeFilter);
159
168
  // the opcua specification doesn't provide dataChangeFilterResult
160
169
  return null;
161
170
  }
162
171
 
163
- function analyseAggregateFilterResult(node: BaseNode, aggregateFilter: AggregateFilter): AggregateFilterResult {
172
+ function analyzeAggregateFilterResult(node: BaseNode, aggregateFilter: AggregateFilter): AggregateFilterResult {
164
173
  assert(aggregateFilter instanceof AggregateFilter);
165
174
  return new AggregateFilterResult({});
166
175
  }
@@ -171,11 +180,11 @@ function _process_filter(node: BaseNode, filter: any): EventFilterResult | Aggre
171
180
  }
172
181
 
173
182
  if (filter instanceof EventFilter) {
174
- return analyseEventFilterResult(node, filter);
183
+ return analyzeEventFilterResult(node, filter);
175
184
  } else if (filter instanceof DataChangeFilter) {
176
- return analyseDataChangeFilterResult(node, filter);
185
+ return analyzeDataChangeFilterResult(node, filter);
177
186
  } else if (filter instanceof AggregateFilter) {
178
- return analyseAggregateFilterResult(node, filter);
187
+ return analyzeAggregateFilterResult(node, filter);
179
188
  }
180
189
  // istanbul ignore next
181
190
  throw new Error("invalid filter");
@@ -270,7 +279,7 @@ function createSubscriptionDiagnostics(subscription: Subscription): Subscription
270
279
  if (!this.$subscription) {
271
280
  return 0;
272
281
  }
273
- return this.$subscription._get_future_sequence_number();
282
+ return this.$subscription.futureSequenceNumber;
274
283
  }
275
284
  );
276
285
  subscription_subscriptionDiagnostics.__defineGetter__(
@@ -466,6 +475,15 @@ export interface ServerCapabilitiesPartial {
466
475
  maxMonitoredItemsPerSubscription: UInt32;
467
476
  }
468
477
 
478
+ export interface IReadAttributeCapable {
479
+ readAttribute(
480
+ context: ISessionContext | null,
481
+ attributeId: AttributeIds,
482
+ indexRange?: NumericRange,
483
+ dataEncoding?: QualifiedNameLike | null
484
+ ): DataValue;
485
+ }
486
+
469
487
  /**
470
488
  * The Subscription class used in the OPCUA server side.
471
489
  */
@@ -564,8 +582,8 @@ export class Subscription extends EventEmitter {
564
582
  }
565
583
 
566
584
  private _life_time_counter: number;
567
- private _keep_alive_counter = 0;
568
- private _pending_notifications: Queue<InternalNotification>;
585
+ protected _keep_alive_counter = 0;
586
+ public _pending_notifications: Queue<InternalNotification>;
569
587
  private _sent_notification_messages: NotificationMessage[];
570
588
  private readonly _sequence_number_generator: SequenceNumberGenerator;
571
589
  private readonly monitoredItems: { [key: number]: MonitoredItem };
@@ -649,7 +667,7 @@ export class Subscription extends EventEmitter {
649
667
  public toString(): string {
650
668
  let str = "Subscription:\n";
651
669
  str += " subscriptionId " + this.id + "\n";
652
- str += " sessionId " + this.getSessionId().toString() + "\n";
670
+ str += " sessionId " + this.getSessionId()?.toString() + "\n";
653
671
 
654
672
  str += " publishingEnabled " + this.publishingEnabled + "\n";
655
673
  str += " maxKeepAliveCount " + this.maxKeepAliveCount + "\n";
@@ -687,6 +705,7 @@ export class Subscription extends EventEmitter {
687
705
  // todo
688
706
  }
689
707
  this._stop_timer();
708
+
690
709
  this._start_timer({ firstTime: false });
691
710
  }
692
711
 
@@ -923,8 +942,8 @@ export class Subscription extends EventEmitter {
923
942
  * number of disabled monitored items.
924
943
  */
925
944
  public get disabledMonitoredItemCount(): number {
926
- return Object.values(this.monitoredItems).reduce((cumul: any, monitoredItem: MonitoredItem) => {
927
- return cumul + (monitoredItem.monitoringMode === MonitoringMode.Disabled ? 1 : 0);
945
+ return Object.values(this.monitoredItems).reduce((sum: number, monitoredItem: MonitoredItem) => {
946
+ return sum + (monitoredItem.monitoringMode === MonitoringMode.Disabled ? 1 : 0);
928
947
  }, 0);
929
948
  }
930
949
 
@@ -941,13 +960,16 @@ export class Subscription extends EventEmitter {
941
960
  * - otherwise the sampling is adjusted
942
961
  * @private
943
962
  */
944
- public adjustSamplingInterval(samplingInterval: number, node: BaseNode): number {
963
+ public adjustSamplingInterval(samplingInterval: number, node?: IReadAttributeCapable): number {
945
964
  if (samplingInterval < 0) {
946
965
  // - The value -1 indicates that the default sampling interval defined by the publishing
947
966
  // interval of the Subscription is requested.
948
967
  // - Any negative number is interpreted as -1.
949
968
  samplingInterval = this.publishingInterval;
950
969
  } else if (samplingInterval === 0) {
970
+ // istanbul ignore next
971
+ if (!node) throw new Error("Internal Error");
972
+
951
973
  // OPCUA 1.0.3 Part 4 - 5.12.1.2
952
974
  // The value 0 indicates that the Server should use the fastest practical rate.
953
975
 
@@ -993,7 +1015,7 @@ export class Subscription extends EventEmitter {
993
1015
  * @param monitoredItemCreateRequest - the parameters describing the monitored Item to create
994
1016
  */
995
1017
  public preCreateMonitoredItem(
996
- addressSpace: AddressSpace,
1018
+ addressSpace: IAddressSpace,
997
1019
  timestampsToReturn: TimestampsToReturn,
998
1020
  monitoredItemCreateRequest: MonitoredItemCreateRequest
999
1021
  ): InternalCreateMonitoredItemResult {
@@ -1088,7 +1110,7 @@ export class Subscription extends EventEmitter {
1088
1110
  }
1089
1111
 
1090
1112
  public async createMonitoredItem(
1091
- addressSpace: AddressSpace,
1113
+ addressSpace: IAddressSpace,
1092
1114
  timestampsToReturn: TimestampsToReturn,
1093
1115
  monitoredItemCreateRequest: MonitoredItemCreateRequest
1094
1116
  ): Promise<MonitoredItemCreateResult> {
@@ -1252,12 +1274,33 @@ export class Subscription extends EventEmitter {
1252
1274
  * @private
1253
1275
  */
1254
1276
  public async resendInitialValues(): Promise<void> {
1255
- const promises: Promise<void>[] = [];
1256
- for (const monitoredItem of Object.values(this.monitoredItems)) {
1257
- assert(monitoredItem.clientHandle !== 4294967295);
1258
- promises.push(monitoredItem.resendInitialValues());
1277
+ this._keep_alive_counter = 0;
1278
+
1279
+ try {
1280
+ const promises: Promise<void>[] = [];
1281
+ for (const monitoredItem of Object.values(this.monitoredItems)) {
1282
+ promises.push(
1283
+ (async () => {
1284
+ try {
1285
+ monitoredItem.resendInitialValue();
1286
+ } catch (err) {
1287
+ warningLog(
1288
+ "resendInitialValues:",
1289
+ monitoredItem.node?.nodeId.toString(),
1290
+ "error:",
1291
+ (err as any).message
1292
+ );
1293
+ }
1294
+ })()
1295
+ );
1296
+ }
1297
+ await Promise.all(promises);
1298
+ } catch (err) {
1299
+ warningLog("resendInitialValues: error:", (err as any).message);
1259
1300
  }
1260
- await Promise.all(promises);
1301
+ // make sure data will be sent immediately
1302
+ this._keep_alive_counter = this.maxKeepAliveCount - 1 ;
1303
+ this.state = SubscriptionState.NORMAL;
1261
1304
  this._harvestMonitoredItems();
1262
1305
  }
1263
1306
 
@@ -1368,7 +1411,7 @@ export class Subscription extends EventEmitter {
1368
1411
  const availableSequenceNumbers = this.getAvailableSequenceNumbers();
1369
1412
  assert(
1370
1413
  !response.notificationMessage ||
1371
- availableSequenceNumbers[availableSequenceNumbers.length - 1] === response.notificationMessage.sequenceNumber
1414
+ availableSequenceNumbers[availableSequenceNumbers.length - 1] === response.notificationMessage.sequenceNumber
1372
1415
  );
1373
1416
  response.availableSequenceNumbers = availableSequenceNumbers;
1374
1417
 
@@ -1380,7 +1423,6 @@ export class Subscription extends EventEmitter {
1380
1423
 
1381
1424
  this.resetLifeTimeAndKeepAliveCounters();
1382
1425
 
1383
-
1384
1426
  // istanbul ignore next
1385
1427
  if (doDebug) {
1386
1428
  debugLog(
@@ -1419,7 +1461,10 @@ export class Subscription extends EventEmitter {
1419
1461
  if (this.hasPendingNotifications) {
1420
1462
  this._publish_pending_notifications();
1421
1463
 
1422
- if (this.state === SubscriptionState.NORMAL && this.hasPendingNotifications) {
1464
+ if (
1465
+ this.state === SubscriptionState.NORMAL ||
1466
+ (this.state === SubscriptionState.LATE && this.hasPendingNotifications)
1467
+ ) {
1423
1468
  // istanbul ignore next
1424
1469
  if (doDebug) {
1425
1470
  debugLog(" -> pendingPublishRequestCount > 0 " + "&& normal state => re-trigger tick event immediately ");
@@ -1433,10 +1478,6 @@ export class Subscription extends EventEmitter {
1433
1478
  }
1434
1479
  }
1435
1480
 
1436
- public _get_future_sequence_number(): number {
1437
- return this._sequence_number_generator ? this._sequence_number_generator.future() : 0;
1438
- }
1439
-
1440
1481
  private _process_keepAlive() {
1441
1482
  this.increaseKeepAliveCounter();
1442
1483
 
@@ -1447,7 +1488,7 @@ export class Subscription extends EventEmitter {
1447
1488
  } else {
1448
1489
  debugLog(
1449
1490
  " -> subscription.state === LATE , " +
1450
- "because keepAlive Response cannot be send due to lack of PublishRequest"
1491
+ "because keepAlive Response cannot be send due to lack of PublishRequest"
1451
1492
  );
1452
1493
  if (this.messageSent || this.keepAliveCounterHasExpired) {
1453
1494
  this.state = SubscriptionState.LATE;
@@ -1483,7 +1524,7 @@ export class Subscription extends EventEmitter {
1483
1524
 
1484
1525
  // make sure that a keep-alive Message will be send at the end of the first publishing cycle
1485
1526
  // if there are no Notifications ready.
1486
- this._keep_alive_counter = 0; // this.maxKeepAliveCount;
1527
+ this._keep_alive_counter = this.maxKeepAliveCount - 1;
1487
1528
 
1488
1529
  if (firstTime) {
1489
1530
  assert(this.messageSent === false);
@@ -1494,10 +1535,19 @@ export class Subscription extends EventEmitter {
1494
1535
  this.timerId = setInterval(this._tick.bind(this), this.publishingInterval);
1495
1536
  }
1496
1537
 
1538
+ private _get_future_sequence_number(): number {
1539
+ return this._sequence_number_generator ? this._sequence_number_generator.future() : 0;
1540
+ }
1541
+ public get futureSequenceNumber(): number {
1542
+ return this._get_future_sequence_number();
1543
+ }
1497
1544
  // counter
1498
1545
  private _get_next_sequence_number(): number {
1499
1546
  return this._sequence_number_generator ? this._sequence_number_generator.next() : 0;
1500
1547
  }
1548
+ public get nextSequenceNumber(): number {
1549
+ return this._get_next_sequence_number();
1550
+ }
1501
1551
 
1502
1552
  /**
1503
1553
  * @private
@@ -1846,7 +1896,7 @@ export class Subscription extends EventEmitter {
1846
1896
  monitoredItem.setMonitoringMode(monitoringMode);
1847
1897
  }
1848
1898
 
1849
- private _harvestMonitoredItems() {
1899
+ public _harvestMonitoredItems() {
1850
1900
  for (const monitoredItem of Object.values(this.monitoredItems)) {
1851
1901
  const notifications_chunks = monitoredItem.extractMonitoredItemNotifications();
1852
1902
  for (const chunk of notifications_chunks) {