node-opcua-server 2.72.2 → 2.74.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 (42) hide show
  1. package/LICENSE +3 -1
  2. package/dist/helper.d.ts +10 -0
  3. package/dist/helper.js +76 -0
  4. package/dist/helper.js.map +1 -0
  5. package/dist/index.d.ts +1 -0
  6. package/dist/index.js +1 -0
  7. package/dist/index.js.map +1 -1
  8. package/dist/monitored_item.js +2 -1
  9. package/dist/monitored_item.js.map +1 -1
  10. package/dist/opcua_server.js +26 -22
  11. package/dist/opcua_server.js.map +1 -1
  12. package/dist/server_capabilities.js +5 -4
  13. package/dist/server_capabilities.js.map +1 -1
  14. package/dist/server_engine.d.ts +1 -1
  15. package/dist/server_engine.js +25 -25
  16. package/dist/server_engine.js.map +1 -1
  17. package/dist/server_publish_engine.d.ts +6 -5
  18. package/dist/server_publish_engine.js +22 -11
  19. package/dist/server_publish_engine.js.map +1 -1
  20. package/dist/server_publish_engine_for_orphan_subscriptions.js +3 -1
  21. package/dist/server_publish_engine_for_orphan_subscriptions.js.map +1 -1
  22. package/dist/server_session.d.ts +4 -3
  23. package/dist/server_session.js +7 -6
  24. package/dist/server_session.js.map +1 -1
  25. package/dist/server_subscription.d.ts +8 -2
  26. package/dist/server_subscription.js +75 -62
  27. package/dist/server_subscription.js.map +1 -1
  28. package/dist/sessions_compatible_for_transfer.d.ts +1 -1
  29. package/dist/sessions_compatible_for_transfer.js +3 -0
  30. package/dist/sessions_compatible_for_transfer.js.map +1 -1
  31. package/package.json +48 -47
  32. package/source/helper.ts +87 -0
  33. package/source/index.ts +2 -1
  34. package/source/monitored_item.ts +3 -2
  35. package/source/opcua_server.ts +27 -26
  36. package/source/server_capabilities.ts +5 -4
  37. package/source/server_engine.ts +29 -27
  38. package/source/server_publish_engine.ts +34 -21
  39. package/source/server_publish_engine_for_orphan_subscriptions.ts +6 -1
  40. package/source/server_session.ts +15 -13
  41. package/source/server_subscription.ts +89 -72
  42. package/source/sessions_compatible_for_transfer.ts +5 -1
@@ -11,7 +11,7 @@ import { checkDebugFlag, make_debugLog } from "node-opcua-debug";
11
11
  import { ObjectRegistry } from "node-opcua-object-registry";
12
12
  import { StatusCode, StatusCodes } from "node-opcua-status-code";
13
13
 
14
- import { PublishRequest, PublishResponse, SubscriptionAcknowledgement } from "node-opcua-types";
14
+ import { PublishRequest, PublishResponse, ServiceFault, SubscriptionAcknowledgement } from "node-opcua-types";
15
15
  import { Subscription } from "./server_subscription";
16
16
  import { SubscriptionState } from "./server_subscription";
17
17
  import { IServerSidePublishEngine, INotifMsg, IClosedOrTransferredSubscription } from "./i_server_side_publish_engine";
@@ -36,7 +36,7 @@ interface PublishData {
36
36
  request: PublishRequest;
37
37
  serverTimeWhenReceived: number;
38
38
  results: StatusCode[];
39
- callback: (request: PublishRequest, response: PublishResponse) => void;
39
+ callback: (request: PublishRequest, response: PublishResponse | ServiceFault) => void;
40
40
  }
41
41
 
42
42
  function _assertValidPublishData(publishData: PublishData) {
@@ -116,7 +116,7 @@ export class ServerSidePublishEngine extends EventEmitter implements IServerSide
116
116
  subscription: Subscription,
117
117
  destPublishEngine: ServerSidePublishEngine,
118
118
  sendInitialValues: boolean
119
- ): Promise<void> {
119
+ ): Promise<Subscription> {
120
120
  const srcPublishEngine = subscription.publishEngine as any as ServerSidePublishEngine;
121
121
 
122
122
  assert(!destPublishEngine.getSubscriptionById(subscription.id));
@@ -135,7 +135,9 @@ export class ServerSidePublishEngine extends EventEmitter implements IServerSide
135
135
  // to the old Session.
136
136
  subscription.notifyTransfer();
137
137
 
138
- destPublishEngine.add_subscription(srcPublishEngine.detach_subscription(subscription));
138
+ const tmp = srcPublishEngine.detach_subscription(subscription);
139
+ destPublishEngine.add_subscription(tmp);
140
+
139
141
  subscription.resetLifeTimeCounter();
140
142
  if (sendInitialValues) {
141
143
  /* A Boolean parameter with the following values:
@@ -158,6 +160,8 @@ export class ServerSidePublishEngine extends EventEmitter implements IServerSide
158
160
 
159
161
  assert(destPublishEngine.getSubscriptionById(subscription.id));
160
162
  assert(!srcPublishEngine.getSubscriptionById(subscription.id));
163
+
164
+ return subscription;
161
165
  }
162
166
 
163
167
  public maxPublishRequestInQueue = 0;
@@ -321,16 +325,17 @@ export class ServerSidePublishEngine extends EventEmitter implements IServerSide
321
325
  }
322
326
 
323
327
  public on_close_subscription(subscription: IClosedOrTransferredSubscription): void {
324
- debugLog("ServerSidePublishEngine#on_close_subscription", subscription.id);
328
+ doDebug && debugLog("ServerSidePublishEngine#on_close_subscription", subscription.id);
325
329
  if (subscription.hasPendingNotifications) {
326
- debugLog(
327
- "ServerSidePublishEngine#on_close_subscription storing subscription",
328
- subscription.id,
329
- " to _closed_subscriptions because it has pending notification"
330
- );
330
+ doDebug &&
331
+ debugLog(
332
+ "ServerSidePublishEngine#on_close_subscription storing subscription",
333
+ subscription.id,
334
+ " to _closed_subscriptions because it has pending notification"
335
+ );
331
336
  this._closed_subscriptions.push(subscription);
332
337
  } else {
333
- debugLog("ServerSidePublishEngine#on_close_subscription disposing subscription", subscription.id);
338
+ doDebug && debugLog("ServerSidePublishEngine#on_close_subscription disposing subscription", subscription.id);
334
339
  // subscription is no longer needed
335
340
  subscription.dispose();
336
341
  }
@@ -357,7 +362,7 @@ export class ServerSidePublishEngine extends EventEmitter implements IServerSide
357
362
  public findLateSubscriptions(): Subscription[] {
358
363
  const subscriptions = Object.values(this._subscriptions);
359
364
  return subscriptions.filter((subscription: Subscription) => {
360
- return subscription.state === SubscriptionState.LATE && subscription.publishingEnabled;
365
+ return (subscription.state === SubscriptionState.LATE || !subscription.messageSent) && subscription.publishingEnabled;
361
366
  });
362
367
  }
363
368
 
@@ -398,7 +403,7 @@ export class ServerSidePublishEngine extends EventEmitter implements IServerSide
398
403
  */
399
404
  public _on_PublishRequest(
400
405
  request: PublishRequest,
401
- callback?: (request1: PublishRequest, response: PublishResponse) => void
406
+ callback?: (request1: PublishRequest, response: PublishResponse| ServiceFault) => void
402
407
  ): void {
403
408
  callback = callback || dummy_function;
404
409
  assert(typeof callback === "function");
@@ -479,6 +484,10 @@ export class ServerSidePublishEngine extends EventEmitter implements IServerSide
479
484
  s.timeToKeepAlive +
480
485
  " m?=" +
481
486
  s.hasUncollectedMonitoredItemNotifications +
487
+ " " +
488
+ SubscriptionState[s.state] +
489
+ " " +
490
+ s.messageSent +
482
491
  "]"
483
492
  )
484
493
  .join(" \n")
@@ -493,6 +502,7 @@ export class ServerSidePublishEngine extends EventEmitter implements IServerSide
493
502
  const starving_subscription = /* this.findSubscriptionWaitingForFirstPublish() || */ findLateSubscriptionSortedByPriority();
494
503
  return starving_subscription;
495
504
  }
505
+
496
506
  private _feed_late_subscription() {
497
507
  setImmediate(() => {
498
508
  if (!this.pendingPublishRequestCount) {
@@ -500,7 +510,8 @@ export class ServerSidePublishEngine extends EventEmitter implements IServerSide
500
510
  }
501
511
  const starving_subscription = this._find_starving_subscription();
502
512
  if (starving_subscription) {
503
- debugLog(chalk.bgWhite.red("feeding most late subscription subscriptionId = "), starving_subscription.id);
513
+ doDebug &&
514
+ debugLog(chalk.bgWhite.red("feeding most late subscription subscriptionId = "), starving_subscription.id);
504
515
  starving_subscription.process_subscription();
505
516
  }
506
517
  });
@@ -529,10 +540,10 @@ export class ServerSidePublishEngine extends EventEmitter implements IServerSide
529
540
 
530
541
  private _send_error_for_request(publishData: PublishData, statusCode: StatusCode): void {
531
542
  _assertValidPublishData(publishData);
532
- const publishResponse = new PublishResponse({
543
+ const response = new ServiceFault({
533
544
  responseHeader: { serviceResult: statusCode }
534
545
  });
535
- this._send_response_for_request(publishData, publishResponse);
546
+ this._send_response_for_request(publishData, response);
536
547
  }
537
548
 
538
549
  private _cancelPendingPublishRequest(statusCode: StatusCode): void {
@@ -621,7 +632,7 @@ export class ServerSidePublishEngine extends EventEmitter implements IServerSide
621
632
  assert(this.pendingPublishRequestCount > 0);
622
633
  assert(response.subscriptionId !== 0xffffff);
623
634
  const publishData = this._publish_request_queue.shift()!;
624
- this._send_response_for_request(publishData, response);
635
+ this._send_valid_response_for_request(publishData, response);
625
636
  }
626
637
 
627
638
  public _on_tick(): void {
@@ -646,15 +657,17 @@ export class ServerSidePublishEngine extends EventEmitter implements IServerSide
646
657
  this._send_error_for_request(publishData, StatusCodes.BadTimeout);
647
658
  }
648
659
  }
649
-
650
- public _send_response_for_request(publishData: PublishData, response: PublishResponse): void {
660
+ public _send_response_for_request(publishData: PublishData, response: PublishResponse | ServiceFault): void {
661
+ response.responseHeader.requestHandle = publishData.request.requestHeader.requestHandle;
662
+ publishData.callback(publishData.request, response);
663
+ }
664
+ public _send_valid_response_for_request(publishData: PublishData, response: PublishResponse): void {
651
665
  if (doDebug) {
652
666
  debugLog("_send_response_for_request ", response.toString());
653
667
  }
654
668
  _assertValidPublishData(publishData);
655
669
  // xx assert(response.responseHeader.requestHandle !== 0,"expecting a valid requestHandle");
656
670
  response.results = publishData.results;
657
- response.responseHeader.requestHandle = publishData.request.requestHeader.requestHandle;
658
- publishData.callback(publishData.request, response);
671
+ this._send_response_for_request(publishData, response);
659
672
  }
660
673
  }
@@ -5,6 +5,7 @@
5
5
  import * as chalk from "chalk";
6
6
 
7
7
  import { checkDebugFlag, make_debugLog } from "node-opcua-debug";
8
+ import { NodeId } from "node-opcua-nodeid";
8
9
 
9
10
  import { ServerSidePublishEngine, ServerSidePublishEngineOptions } from "./server_publish_engine";
10
11
  import { Subscription } from "./server_subscription";
@@ -28,6 +29,10 @@ export class ServerSidePublishEngineForOrphanSubscription extends ServerSidePubl
28
29
 
29
30
  public add_subscription(subscription: Subscription): Subscription {
30
31
  debugLog(chalk.bgCyan.yellow.bold(" adding live subscription with id="), subscription.id, " to orphan");
32
+
33
+ // detach subscription from old seession
34
+ subscription.$session = undefined;
35
+
31
36
  super.add_subscription(subscription);
32
37
  // also add an event handler to detected when the subscription has ended
33
38
  // so we can automatically remove it from the orphan table
@@ -38,7 +43,7 @@ export class ServerSidePublishEngineForOrphanSubscription extends ServerSidePubl
38
43
  // xx publish_engine.detach_subscription(subscription);
39
44
  // Xx subscription.dispose();
40
45
  };
41
- subscription.on("expired", (subscription as any)._expired_func);
46
+ subscription.once("expired", (subscription as any)._expired_func);
42
47
  return subscription;
43
48
  }
44
49
 
@@ -72,6 +72,7 @@ interface SessionSecurityDiagnosticsDataTypeEx extends SessionSecurityDiagnostic
72
72
  $session: any;
73
73
  }
74
74
 
75
+ export type SessionStatus = "new" | "active" | "screwed" | "disposed" | "closed";
75
76
  /**
76
77
  *
77
78
  * A Server session object.
@@ -99,13 +100,12 @@ export class ServerSession extends EventEmitter implements ISubscriber, ISession
99
100
  public static registry = new ObjectRegistry();
100
101
  public static maxPublishRequestInQueue = 100;
101
102
 
102
- public __status = "";
103
+ public __status: SessionStatus = "new";
103
104
  public parent: ServerEngine;
104
105
  public authenticationToken: NodeId;
105
106
  public nodeId: NodeId;
106
107
  public sessionName = "";
107
108
 
108
-
109
109
  public publishEngine: ServerSidePublishEngine;
110
110
  public sessionObject: any;
111
111
  public readonly creationDate: Date;
@@ -227,18 +227,21 @@ export class ServerSession extends EventEmitter implements ISubscriber, ISession
227
227
  * the first transaction is the creation of the session
228
228
  */
229
229
  public get clientLastContactTime(): number {
230
- const lastSeen = this._watchDogData ? this._watchDogData.lastSeen : minOPCUADate.getTime();
230
+ const lastSeen = this._watchDogData ? this._watchDogData.lastSeen : minOPCUADate.getTime();
231
231
  return WatchDog.lastSeenToDuration(lastSeen);
232
232
  }
233
233
 
234
- public get status(): string {
234
+ public get status(): SessionStatus {
235
235
  return this.__status;
236
236
  }
237
237
 
238
- public set status(value: string) {
238
+ public set status(value: SessionStatus) {
239
239
  if (value === "active") {
240
240
  this._createSessionObjectInAddressSpace();
241
241
  }
242
+ if (this.__status !== value) {
243
+ this.emit("statusChanged", value);
244
+ }
242
245
  this.__status = value;
243
246
  }
244
247
 
@@ -304,7 +307,7 @@ export class ServerSession extends EventEmitter implements ISubscriber, ISession
304
307
  const propName = lowerFirstLetter(counterName + "Count");
305
308
  // istanbul ignore next
306
309
  if (!Object.prototype.hasOwnProperty.call(this._sessionDiagnostics, propName)) {
307
- errorLog("incrementRequestTotalCounter: cannot find", propName);
310
+ errorLog("incrementRequestTotalCounter: cannot find", propName);
308
311
  // xx return;
309
312
  } else {
310
313
  (this._sessionDiagnostics as any)[propName].totalCount += 1;
@@ -444,7 +447,7 @@ export class ServerSession extends EventEmitter implements ISubscriber, ISession
444
447
  assert(this.currentSubscriptionCount === 0);
445
448
 
446
449
  this.status = "closed";
447
-
450
+
448
451
  this._detach_channel();
449
452
 
450
453
  /**
@@ -559,10 +562,10 @@ export class ServerSession extends EventEmitter implements ISubscriber, ISession
559
562
 
560
563
  public _detach_channel(): void {
561
564
  const channel = this.channel;
562
-
565
+
563
566
  // istanbul ignore next
564
567
  if (!channel) {
565
- return;
568
+ return;
566
569
  // already detached !
567
570
  // throw new Error("expecting a valid channel");
568
571
  }
@@ -682,13 +685,13 @@ export class ServerSession extends EventEmitter implements ISubscriber, ISession
682
685
 
683
686
  Object.defineProperty(this._sessionDiagnostics, "sessionId", {
684
687
  get(this: SessionDiagnosticsDataTypeEx) {
685
- return this.$session?.nodeId;
688
+ return this.$session ? this.$session.nodeId : NodeId.nullNodeId;
686
689
  }
687
690
  });
688
691
 
689
692
  Object.defineProperty(this._sessionDiagnostics, "sessionName", {
690
693
  get(this: SessionDiagnosticsDataTypeEx) {
691
- return this.$session?.sessionName.toString();
694
+ return this.$session ? this.$session.sessionName.toString() : "";
692
695
  }
693
696
  });
694
697
 
@@ -910,8 +913,7 @@ export class ServerSession extends EventEmitter implements ISubscriber, ISession
910
913
  assert(this.nodeId instanceof NodeId);
911
914
 
912
915
  subscription.$session = this;
913
-
914
- subscription.sessionId = this.nodeId;
916
+ assert(subscription.sessionId === this.nodeId);
915
917
 
916
918
  this._cumulatedSubscriptionCount += 1;
917
919