node-opcua-server 2.120.0 → 2.122.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 (49) hide show
  1. package/dist/addressSpace_accessor.js +1 -3
  2. package/dist/addressSpace_accessor.js.map +1 -1
  3. package/dist/base_server.js.map +1 -1
  4. package/dist/factory.js.map +1 -1
  5. package/dist/helper.js.map +1 -1
  6. package/dist/history_server_capabilities.js.map +1 -1
  7. package/dist/monitored_item.d.ts +6 -6
  8. package/dist/monitored_item.js +55 -38
  9. package/dist/monitored_item.js.map +1 -1
  10. package/dist/node_sampler.js.map +1 -1
  11. package/dist/opcua_server.d.ts +4 -3
  12. package/dist/opcua_server.js +16 -19
  13. package/dist/opcua_server.js.map +1 -1
  14. package/dist/queue.js.map +1 -1
  15. package/dist/register_server_manager.js +1 -1
  16. package/dist/register_server_manager.js.map +1 -1
  17. package/dist/register_server_manager_mdns_only.js +1 -1
  18. package/dist/register_server_manager_mdns_only.js.map +1 -1
  19. package/dist/server_capabilities.js +2 -2
  20. package/dist/server_capabilities.js.map +1 -1
  21. package/dist/server_end_point.js +13 -9
  22. package/dist/server_end_point.js.map +1 -1
  23. package/dist/server_engine.d.ts +3 -3
  24. package/dist/server_engine.js +3 -3
  25. package/dist/server_engine.js.map +1 -1
  26. package/dist/server_publish_engine.d.ts +1 -4
  27. package/dist/server_publish_engine.js +95 -91
  28. package/dist/server_publish_engine.js.map +1 -1
  29. package/dist/server_publish_engine_for_orphan_subscriptions.js +1 -1
  30. package/dist/server_publish_engine_for_orphan_subscriptions.js.map +1 -1
  31. package/dist/server_session.js +3 -1
  32. package/dist/server_session.js.map +1 -1
  33. package/dist/server_subscription.d.ts +22 -14
  34. package/dist/server_subscription.js +46 -21
  35. package/dist/server_subscription.js.map +1 -1
  36. package/dist/sessions_compatible_for_transfer.js.map +1 -1
  37. package/dist/user_manager.js.map +1 -1
  38. package/dist/user_manager_ua.js.map +1 -1
  39. package/dist/validate_filter.js.map +1 -1
  40. package/package.json +43 -43
  41. package/source/addressSpace_accessor.ts +2 -3
  42. package/source/monitored_item.ts +68 -56
  43. package/source/opcua_server.ts +24 -21
  44. package/source/server_end_point.ts +13 -7
  45. package/source/server_engine.ts +6 -5
  46. package/source/server_publish_engine.ts +14 -12
  47. package/source/server_publish_engine_for_orphan_subscriptions.ts +1 -1
  48. package/source/server_session.ts +2 -2
  49. package/source/server_subscription.ts +84 -34
@@ -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) {