node-opcua-server 2.120.0 → 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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-opcua-server",
3
- "version": "2.120.0",
3
+ "version": "2.121.0",
4
4
  "description": "pure nodejs OPCUA SDK - module server",
5
5
  "scripts": {
6
6
  "build": "tsc -b",
@@ -17,54 +17,54 @@
17
17
  "chalk": "4.1.2",
18
18
  "dequeue": "^1.0.5",
19
19
  "lodash": "4.17.21",
20
- "node-opcua-address-space": "2.120.0",
21
- "node-opcua-address-space-base": "2.120.0",
20
+ "node-opcua-address-space": "2.121.0",
21
+ "node-opcua-address-space-base": "2.121.0",
22
22
  "node-opcua-assert": "2.120.0",
23
- "node-opcua-basic-types": "2.120.0",
23
+ "node-opcua-basic-types": "2.121.0",
24
24
  "node-opcua-binary-stream": "2.120.0",
25
- "node-opcua-certificate-manager": "2.120.0",
26
- "node-opcua-client": "2.120.0",
27
- "node-opcua-client-dynamic-extension-object": "2.120.0",
28
- "node-opcua-common": "2.120.0",
29
- "node-opcua-constants": "2.120.0",
30
- "node-opcua-crypto": "4.7.0",
31
- "node-opcua-data-model": "2.120.0",
32
- "node-opcua-data-value": "2.120.0",
33
- "node-opcua-date-time": "2.120.0",
34
- "node-opcua-debug": "2.120.0",
35
- "node-opcua-extension-object": "2.120.0",
36
- "node-opcua-factory": "2.120.0",
25
+ "node-opcua-certificate-manager": "2.121.0",
26
+ "node-opcua-client": "2.121.0",
27
+ "node-opcua-client-dynamic-extension-object": "2.121.0",
28
+ "node-opcua-common": "2.121.0",
29
+ "node-opcua-constants": "2.121.0",
30
+ "node-opcua-crypto": "4.8.0",
31
+ "node-opcua-data-model": "2.121.0",
32
+ "node-opcua-data-value": "2.121.0",
33
+ "node-opcua-date-time": "2.121.0",
34
+ "node-opcua-debug": "2.121.0",
35
+ "node-opcua-extension-object": "2.121.0",
36
+ "node-opcua-factory": "2.121.0",
37
37
  "node-opcua-hostname": "2.120.0",
38
- "node-opcua-nodeid": "2.120.0",
39
- "node-opcua-nodesets": "2.120.0",
40
- "node-opcua-numeric-range": "2.120.0",
41
- "node-opcua-object-registry": "2.120.0",
42
- "node-opcua-secure-channel": "2.120.0",
43
- "node-opcua-service-browse": "2.120.0",
44
- "node-opcua-service-call": "2.120.0",
45
- "node-opcua-service-discovery": "2.120.0",
46
- "node-opcua-service-endpoints": "2.120.0",
47
- "node-opcua-service-filter": "2.120.0",
48
- "node-opcua-service-history": "2.120.0",
49
- "node-opcua-service-node-management": "2.120.0",
50
- "node-opcua-service-query": "2.120.0",
51
- "node-opcua-service-read": "2.120.0",
52
- "node-opcua-service-register-node": "2.120.0",
53
- "node-opcua-service-secure-channel": "2.120.0",
54
- "node-opcua-service-session": "2.120.0",
55
- "node-opcua-service-subscription": "2.120.0",
56
- "node-opcua-service-translate-browse-path": "2.120.0",
57
- "node-opcua-service-write": "2.120.0",
58
- "node-opcua-status-code": "2.120.0",
59
- "node-opcua-transport": "2.120.0",
60
- "node-opcua-types": "2.120.0",
38
+ "node-opcua-nodeid": "2.121.0",
39
+ "node-opcua-nodesets": "2.121.0",
40
+ "node-opcua-numeric-range": "2.121.0",
41
+ "node-opcua-object-registry": "2.121.0",
42
+ "node-opcua-secure-channel": "2.121.0",
43
+ "node-opcua-service-browse": "2.121.0",
44
+ "node-opcua-service-call": "2.121.0",
45
+ "node-opcua-service-discovery": "2.121.0",
46
+ "node-opcua-service-endpoints": "2.121.0",
47
+ "node-opcua-service-filter": "2.121.0",
48
+ "node-opcua-service-history": "2.121.0",
49
+ "node-opcua-service-node-management": "2.121.0",
50
+ "node-opcua-service-query": "2.121.0",
51
+ "node-opcua-service-read": "2.121.0",
52
+ "node-opcua-service-register-node": "2.121.0",
53
+ "node-opcua-service-secure-channel": "2.121.0",
54
+ "node-opcua-service-session": "2.121.0",
55
+ "node-opcua-service-subscription": "2.121.0",
56
+ "node-opcua-service-translate-browse-path": "2.121.0",
57
+ "node-opcua-service-write": "2.121.0",
58
+ "node-opcua-status-code": "2.121.0",
59
+ "node-opcua-transport": "2.121.0",
60
+ "node-opcua-types": "2.121.0",
61
61
  "node-opcua-utils": "2.120.0",
62
- "node-opcua-variant": "2.120.0",
62
+ "node-opcua-variant": "2.121.0",
63
63
  "thenify": "^3.3.1"
64
64
  },
65
65
  "devDependencies": {
66
- "node-opcua-data-access": "2.120.0",
67
- "node-opcua-leak-detector": "2.120.0",
66
+ "node-opcua-data-access": "2.121.0",
67
+ "node-opcua-leak-detector": "2.121.0",
68
68
  "node-opcua-test-helpers": "2.120.0",
69
69
  "should": "^13.2.3",
70
70
  "sinon": "^17.0.1",
@@ -85,7 +85,7 @@
85
85
  "internet of things"
86
86
  ],
87
87
  "homepage": "http://node-opcua.github.io/",
88
- "gitHead": "f4ae243bb61ba7af4c019a3cfe76dfc0f6427983",
88
+ "gitHead": "ba93a78eda343ecc0cc20476837347f6dfa259e0",
89
89
  "files": [
90
90
  "dist",
91
91
  "source"
@@ -185,7 +185,7 @@ export class AddressSpaceAccessor implements IAddressSpaceAccessor, IAddressSpac
185
185
  const continuationPoint = m.nodeToRead.continuationPoint;
186
186
  return await this.historyReadNode(context, m.nodeToRead, m.processDetail, timestampsToReturn, {
187
187
  continuationPoint,
188
- releaseContinuationPoints /**, index = ??? */
188
+ releaseContinuationPoints
189
189
  });
190
190
  };
191
191
 
@@ -217,7 +217,6 @@ export class AddressSpaceAccessor implements IAddressSpaceAccessor, IAddressSpac
217
217
  return await this.historyReadNode(context, nodeToRead, historyReadDetails, timestampsToReturn, {
218
218
  continuationPoint,
219
219
  releaseContinuationPoints,
220
- index
221
220
  });
222
221
  };
223
222
  const promises: Promise<HistoryReadResult>[] = [];
@@ -405,7 +404,7 @@ export class AddressSpaceAccessor implements IAddressSpaceAccessor, IAddressSpac
405
404
  // invalid attributes : BadNodeAttributesInvalid
406
405
  // invalid range : BadIndexRangeInvalid
407
406
  const result = await obj.historyRead(context, historyReadDetails, indexRange, dataEncoding, continuationData);
408
- assert(result!.statusCode instanceof StatusCode);
407
+
409
408
  assert(result!.isValid());
410
409
  return result;
411
410
  }
@@ -255,22 +255,21 @@ function apply_dataChange_filter(this: MonitoredItem, newDataValue: DataValue, o
255
255
 
256
256
  const s = (a: any) => JSON.stringify(a, null, " ");
257
257
 
258
- function safeGuardRegiter(monitoredItem: MonitoredItem) {
259
-
258
+ function safeGuardRegister(monitoredItem: MonitoredItem) {
260
259
  (monitoredItem.oldDataValue as any)._$monitoredItem = monitoredItem.node?.nodeId?.toString();
261
260
  (monitoredItem as any)._$safeGuard = s((monitoredItem as any).oldDataValue);
262
261
  }
263
262
  function safeGuardVerify(monitoredItem: MonitoredItem) {
264
263
  if ((monitoredItem as any)._$safeGuard) {
265
- const verif = s(monitoredItem.oldDataValue|| "");
264
+ const verif = s(monitoredItem.oldDataValue || "");
266
265
  if (verif !== (monitoredItem as any)._$safeGuard) {
267
266
  errorLog(verif, (monitoredItem as any)._$safeGuard);
268
- throw new Error("Internal error: DataValue has been altereed !!!");
267
+ throw new Error("Internal error: DataValue has been altered !!!");
269
268
  }
270
269
  }
271
270
  }
272
271
  function apply_filter(this: MonitoredItem, newDataValue: DataValue) {
273
- if (!this.oldDataValue) {
272
+ if (this.oldDataValue === badDataUnavailable) {
274
273
  return true; // keep
275
274
  }
276
275
 
@@ -365,6 +364,7 @@ function isSourceNewerThan(a: DataValue, b?: DataValue): boolean {
365
364
  return at > bt;
366
365
  }
367
366
 
367
+ const badDataUnavailable = new DataValue({ statusCode: StatusCodes.BadDataUnavailable }); // unset initially
368
368
  /**
369
369
  * a server side monitored item
370
370
  *
@@ -391,7 +391,7 @@ export class MonitoredItem extends EventEmitter {
391
391
  public samplingInterval = -1;
392
392
  public monitoredItemId: number;
393
393
  public overflow: boolean;
394
- public oldDataValue?: DataValue;
394
+ public oldDataValue: DataValue;
395
395
  public monitoringMode: MonitoringMode;
396
396
  public timestampsToReturn: TimestampsToReturn;
397
397
  public itemToMonitor: any;
@@ -404,7 +404,7 @@ export class MonitoredItem extends EventEmitter {
404
404
  public samplingFunc: SamplingFunc | null = null;
405
405
 
406
406
  private _node: BaseNode | null;
407
- private queue: QueueItem[];
407
+ public queue: QueueItem[];
408
408
  private _semantic_version: number;
409
409
  private _is_sampling = false;
410
410
  private _on_opcua_event_received_callback: any;
@@ -433,7 +433,7 @@ export class MonitoredItem extends EventEmitter {
433
433
  this.queue = [];
434
434
  this.overflow = false;
435
435
 
436
- this.oldDataValue = new DataValue({ statusCode: StatusCodes.BadDataUnavailable }); // unset initially
436
+ this.oldDataValue = badDataUnavailable;
437
437
 
438
438
  // user has to call setMonitoringMode
439
439
  this.monitoringMode = MonitoringMode.Invalid;
@@ -522,7 +522,7 @@ export class MonitoredItem extends EventEmitter {
522
522
  }
523
523
 
524
524
  // x assert(this._samplingId === null,"Sampling Id must be null");
525
- this.oldDataValue = undefined;
525
+ this.oldDataValue = badDataUnavailable;
526
526
  this.queue = [];
527
527
  this.itemToMonitor = null;
528
528
  this.filter = null;
@@ -580,7 +580,7 @@ export class MonitoredItem extends EventEmitter {
580
580
  *
581
581
  */
582
582
  // eslint-disable-next-line complexity, max-statements
583
- public recordValue(dataValue: DataValue, skipChangeTest: boolean, indexRange?: NumericRange): boolean {
583
+ public recordValue(dataValue: DataValue, skipChangeTest?: boolean, indexRange?: NumericRange): boolean {
584
584
  if (!this.itemToMonitor) {
585
585
  // we must have a valid itemToMonitor(have this monitoredItem been disposed already ?)
586
586
  // istanbul ignore next
@@ -588,18 +588,13 @@ export class MonitoredItem extends EventEmitter {
588
588
  return false;
589
589
  }
590
590
 
591
- assert(dataValue !== this.oldDataValue, "recordValue expects different dataValue to be provided");
592
-
593
- assert(
594
- !dataValue.value || !this.oldDataValue || dataValue.value !== this.oldDataValue!.value,
595
- "recordValue expects different dataValue.value to be provided"
596
- );
597
-
598
- assert(!dataValue.value || dataValue.value.isValid(), "expecting a valid variant value");
591
+ if (dataValue === this.oldDataValue) {
592
+ errorLog("recordValue expects different dataValue to be provided");
593
+ }
594
+ doDebug && assert(!dataValue.value || dataValue.value.isValid(), "expecting a valid variant value");
599
595
 
600
596
  const hasSemanticChanged = this.node && (this.node as any).semantic_version !== this._semantic_version;
601
597
 
602
-
603
598
  if (!hasSemanticChanged && indexRange && this.itemToMonitor.indexRange) {
604
599
  // we just ignore changes that do not fall within our range
605
600
  // ( unless semantic bit has changed )
@@ -655,13 +650,13 @@ export class MonitoredItem extends EventEmitter {
655
650
  }
656
651
  }
657
652
 
658
- if (!apply_filter.call(this, dataValue)) {
653
+ if (!skipChangeTest && !apply_filter.call(this, dataValue)) {
659
654
  // istanbul ignore next
660
655
  if (doDebug) {
661
656
  debugLog("recordValue => Rejected ", this.node?.browseName.toString(), " because apply_filter");
662
657
  debugLog("current Value =>", this.oldDataValue?.toString());
663
- debugLog("propoped Value =>", dataValue?.toString());
664
- debugLog("propoped Value =>", dataValue == this.oldDataValue, dataValue.value === this.oldDataValue?.value);
658
+ debugLog("proposed Value =>", dataValue?.toString());
659
+ debugLog("proposed Value =>", dataValue == this.oldDataValue, dataValue.value === this.oldDataValue?.value);
665
660
  }
666
661
  return false;
667
662
  }
@@ -672,12 +667,12 @@ export class MonitoredItem extends EventEmitter {
672
667
 
673
668
  // istanbul ignore next
674
669
  if (doDebug) {
675
- debugLog("Current : ", this.oldDataValue!.toString());
670
+ debugLog("Current : ", this.oldDataValue?.toString());
676
671
  debugLog("New : ", dataValue.toString());
677
672
  debugLog("indexRange=", indexRange);
678
673
  }
679
674
 
680
- if (sameVariant(dataValue.value, this.oldDataValue!.value)) {
675
+ if (this.oldDataValue !== badDataUnavailable && sameVariant(dataValue.value, this.oldDataValue.value)) {
681
676
  // istanbul ignore next
682
677
  doDebug &&
683
678
  debugLog("recordValue => Rejected ", this.node?.browseName.toString(), " because useIndexRange && sameVariant");
@@ -791,13 +786,24 @@ export class MonitoredItem extends EventEmitter {
791
786
  return notifications;
792
787
  }
793
788
 
794
- public modify(timestampsToReturn: TimestampsToReturn, monitoringParameters: MonitoringParameters): MonitoredItemModifyResult {
789
+ public modify(
790
+ timestampsToReturn: TimestampsToReturn | null,
791
+ monitoringParameters: MonitoringParameters | null
792
+ ): MonitoredItemModifyResult {
795
793
  assert(monitoringParameters instanceof MonitoringParameters);
796
794
 
797
795
  const old_samplingInterval = this.samplingInterval;
798
796
 
799
797
  this.timestampsToReturn = timestampsToReturn || this.timestampsToReturn;
800
798
 
799
+ if (!monitoringParameters) {
800
+ return new MonitoredItemModifyResult({
801
+ revisedQueueSize: this.queueSize,
802
+ revisedSamplingInterval: this.samplingInterval,
803
+ filterResult: null,
804
+ statusCode: StatusCodes.Good
805
+ });
806
+ }
801
807
  if (old_samplingInterval !== 0 && monitoringParameters.samplingInterval === 0) {
802
808
  monitoringParameters.samplingInterval = MonitoredItem.minimumSamplingInterval; // fastest possible
803
809
  }
@@ -831,13 +837,30 @@ export class MonitoredItem extends EventEmitter {
831
837
  });
832
838
  }
833
839
 
834
- public async resendInitialValues(): Promise<void> {
840
+ public async resendInitialValue(): Promise<void> {
835
841
  // the first Publish response(s) after the TransferSubscriptions call shall contain the current values of all
836
842
  // Monitored Items in the Subscription where the Monitoring Mode is set to Reporting.
837
843
  // the first Publish response after the TransferSubscriptions call shall contain only the value changes since
838
844
  // the last Publish response was sent.
839
845
  // This parameter only applies to MonitoredItems used for monitoring Attribute changes.
840
- return this._start_sampling(true);
846
+
847
+ // istanbul ignore next
848
+ if (!this.node) return;
849
+
850
+ const sessionContext = this.getSessionContext() || SessionContext.defaultContext;
851
+
852
+ // istanbul ignore next
853
+ if (!sessionContext) return;
854
+
855
+ // no need to resend if a value is already in the queue
856
+ if (this.queue.length > 0) return;
857
+
858
+ const theValueToResend =
859
+ this.oldDataValue !== badDataUnavailable
860
+ ? this.oldDataValue
861
+ : this.node.readAttribute(sessionContext, this.itemToMonitor.attributeId);
862
+ this.oldDataValue = badDataUnavailable;
863
+ this._enqueue_value(theValueToResend);
841
864
  }
842
865
 
843
866
  private getSessionContext(): ISessionContext | null {
@@ -857,8 +880,11 @@ export class MonitoredItem extends EventEmitter {
857
880
  return;
858
881
  }
859
882
 
860
- const sessionContext = this.getSessionContext();
883
+ // Use default context if session is not available
884
+ const sessionContext = this.getSessionContext() || SessionContext.defaultContext;
885
+
861
886
  if (!sessionContext) {
887
+ warningLog("MonitoredItem#_on_sampling_timer : ", this.node?.nodeId.toString(), "cannot find session");
862
888
  return;
863
889
  }
864
890
  // istanbul ignore next
@@ -893,13 +919,13 @@ export class MonitoredItem extends EventEmitter {
893
919
 
894
920
  this._is_sampling = true;
895
921
 
896
- this.samplingFunc.call(this, sessionContext, this.oldDataValue!, (err: Error | null, newDataValue?: DataValue) => {
922
+ this.samplingFunc.call(this, sessionContext, this.oldDataValue, (err: Error | null, newDataValue?: DataValue) => {
897
923
  if (!this._samplingId) {
898
924
  // item has been disposed. The monitored item has been disposed while the async sampling func
899
925
  // was taking place ... just ignore this
900
926
  return;
901
927
  }
902
- // istanbull ignore next
928
+ // istanbul ignore next
903
929
  if (err) {
904
930
  errorLog(" SAMPLING ERROR =>", err);
905
931
  } else {
@@ -1022,13 +1048,14 @@ export class MonitoredItem extends EventEmitter {
1022
1048
  private _start_sampling(recordInitialValue: boolean): void {
1023
1049
  // istanbul ignore next
1024
1050
  if (!this.node) {
1025
- throw new Error("Internal Error");
1051
+ return; // we just want to ignore here ...
1026
1052
  }
1053
+ this.oldDataValue = badDataUnavailable;
1027
1054
  setImmediate(() => this.__start_sampling(recordInitialValue));
1028
1055
  }
1029
1056
 
1030
1057
  private __acquireInitialValue(sessionContext: ISessionContext, callback: CallbackT<DataValue>): void {
1031
- // aquire initial value from the variable/object not itself or from the last known value if we have
1058
+ // acquire initial value from the variable/object not itself or from the last known value if we have
1032
1059
  // one already
1033
1060
  assert(this.itemToMonitor.attributeId === AttributeIds.Value);
1034
1061
  assert(this.node);
@@ -1036,16 +1063,16 @@ export class MonitoredItem extends EventEmitter {
1036
1063
  return callback(new Error("Invalid "));
1037
1064
  }
1038
1065
  const variable = this.node as UAVariable;
1039
- if (!this.oldDataValue || this.oldDataValue.statusCode.value == StatusCodes.BadDataUnavailable.value) {
1066
+ if (this.oldDataValue == badDataUnavailable) {
1040
1067
  variable.readValueAsync(sessionContext, (err: Error | null, dataValue?: DataValue) => {
1041
1068
  callback(err, dataValue);
1042
1069
  });
1043
1070
  } else {
1044
1071
  const o = this.oldDataValue;
1045
- this.oldDataValue = new DataValue({ statusCode: StatusCodes.BadDataUnavailable });
1072
+ this.oldDataValue = badDataUnavailable;
1046
1073
  // istanbul ignore next
1047
1074
  if (doDebug) {
1048
- safeGuardRegiter(this);
1075
+ safeGuardRegister(this);
1049
1076
  }
1050
1077
  callback(null, o);
1051
1078
  }
@@ -1056,7 +1083,7 @@ export class MonitoredItem extends EventEmitter {
1056
1083
  return; // we just want to ignore here ...
1057
1084
  }
1058
1085
 
1059
- const sessionContext = this.getSessionContext();
1086
+ const sessionContext = this.getSessionContext() || SessionContext.defaultContext;
1060
1087
  // istanbul ignore next
1061
1088
  if (!sessionContext) {
1062
1089
  return;
@@ -1241,34 +1268,20 @@ export class MonitoredItem extends EventEmitter {
1241
1268
  * @param dataValue {DataValue} the dataValue to enqueue
1242
1269
  * @private
1243
1270
  */
1244
- private _enqueue_value(dataValue: DataValue) {
1271
+ public _enqueue_value(dataValue: DataValue) {
1245
1272
  // preconditions:
1246
- if (doDebug) {
1247
- debugLog("_enqueue_value = ", dataValue.toString());
1248
- }
1273
+ doDebug && debugLog("_enqueue_value = ", dataValue.toString());
1249
1274
 
1250
- assert(dataValue instanceof DataValue);
1251
1275
  // lets verify that, if status code is good then we have a valid Variant in the dataValue
1252
- assert(!dataValue.statusCode.isGoodish() || dataValue.value instanceof Variant);
1253
- // xx assert(isGoodish(dataValue.statusCode) || util.isNullOrUndefined(dataValue.value) );
1276
+ doDebug && assert(!dataValue.statusCode.isGoodish() || dataValue.value instanceof Variant);
1254
1277
  // let's check that data Value is really a different object
1255
1278
  // we may end up with corrupted queue if dataValue are recycled and stored as is in notifications
1256
- assert(dataValue !== this.oldDataValue, "dataValue cannot be the same object twice!");
1257
-
1258
- // Xx // todo ERN !!!! PLEASE CHECK this !!!
1259
- // Xx // let make a clone, so we have a snapshot
1260
- // Xx dataValue = dataValue.clone();
1279
+ doDebug && assert(dataValue !== this.oldDataValue, "dataValue cannot be the same object twice!");
1261
1280
 
1262
1281
  // let's check that data Value is really a different object
1263
1282
  // we may end up with corrupted queue if dataValue are recycled and stored as is in notifications
1264
- assert(
1265
- !this.oldDataValue || !dataValue.value || dataValue.value !== this.oldDataValue.value,
1266
- "dataValue cannot be the same object twice!"
1267
- );
1268
-
1269
1283
  if (
1270
1284
  !(
1271
- !this.oldDataValue ||
1272
1285
  !this.oldDataValue.value ||
1273
1286
  !dataValue.value ||
1274
1287
  !(dataValue.value.value instanceof Object) ||
@@ -1294,7 +1307,7 @@ export class MonitoredItem extends EventEmitter {
1294
1307
  this.oldDataValue = dataValue.clone();
1295
1308
  // istanbul ignore next
1296
1309
  if (doDebug) {
1297
- safeGuardRegiter(this);
1310
+ safeGuardRegister(this);
1298
1311
  }
1299
1312
 
1300
1313
  const notification = this._makeDataChangeNotification(this.oldDataValue);
@@ -1302,7 +1315,6 @@ export class MonitoredItem extends EventEmitter {
1302
1315
  }
1303
1316
 
1304
1317
  private _makeEventFieldList(eventFields: any[]): EventFieldList {
1305
- assert(Array.isArray(eventFields));
1306
1318
  return new EventFieldList({
1307
1319
  clientHandle: this.clientHandle,
1308
1320
  eventFields
@@ -1479,8 +1479,9 @@ export class OPCUAServer extends OPCUABaseServer {
1479
1479
  /**
1480
1480
  * retrieve a session by authentication token
1481
1481
  * @internal
1482
+ * @private
1482
1483
  */
1483
- protected getSession(authenticationToken: NodeId, activeOnly?: boolean): ServerSession | null {
1484
+ public getSession(authenticationToken: NodeId, activeOnly?: boolean): ServerSession | null {
1484
1485
  return this.engine ? this.engine.getSession(authenticationToken, activeOnly) : null;
1485
1486
  }
1486
1487
 
@@ -1621,8 +1622,7 @@ export class OPCUAServer extends OPCUABaseServer {
1621
1622
  case StatusCodes.BadCertificateIssuerTimeInvalid:
1622
1623
  this.raiseEvent("AuditCertificateExpiredEventType", {
1623
1624
  certificate: { dataType: DataType.ByteString, value: certificate },
1624
- sourceName: { dataType: DataType.String, value: "Security/Certificate" },
1625
- comment: { dataType: DataType.String, value: certificateStatus.toString() }
1625
+ sourceName: { dataType: DataType.String, value: "Security/Certificate" }
1626
1626
  });
1627
1627
  break;
1628
1628
  case StatusCodes.BadCertificateRevoked:
@@ -1630,8 +1630,7 @@ export class OPCUAServer extends OPCUABaseServer {
1630
1630
  case StatusCodes.BadCertificateIssuerRevocationUnknown:
1631
1631
  this.raiseEvent("AuditCertificateRevokedEventType", {
1632
1632
  certificate: { dataType: DataType.ByteString, value: certificate },
1633
- sourceName: { dataType: DataType.String, value: "Security/Certificate" },
1634
- comment: { dataType: DataType.String, value: certificateStatus.toString() }
1633
+ sourceName: { dataType: DataType.String, value: "Security/Certificate" }
1635
1634
  });
1636
1635
  break;
1637
1636
  case StatusCodes.BadCertificateIssuerUseNotAllowed:
@@ -1639,8 +1638,7 @@ export class OPCUAServer extends OPCUABaseServer {
1639
1638
  case StatusCodes.BadSecurityChecksFailed:
1640
1639
  this.raiseEvent("AuditCertificateMismatchEventType", {
1641
1640
  certificate: { dataType: DataType.ByteString, value: certificate },
1642
- sourceName: { dataType: DataType.String, value: "Security/Certificate" },
1643
- comment: { dataType: DataType.String, value: certificateStatus.toString() }
1641
+ sourceName: { dataType: DataType.String, value: "Security/Certificate" }
1644
1642
  });
1645
1643
  break;
1646
1644
  }
@@ -2347,8 +2345,8 @@ export class OPCUAServer extends OPCUABaseServer {
2347
2345
  let response: any;
2348
2346
  /* istanbul ignore next */
2349
2347
  if (!message.session || message.session_statusCode !== StatusCodes.Good) {
2350
- const errMessage = "INVALID SESSION !! ";
2351
- response = new ResponseClass({ responseHeader: { serviceResult: message.session_statusCode } });
2348
+ const errMessage = "=>" + message.session_statusCode?.toString();
2349
+ response = new ServiceFault({ responseHeader: { serviceResult: message.session_statusCode } });
2352
2350
  debugLog(chalk.red.bold(errMessage), chalk.yellow(message.session_statusCode!.toString()), response.constructor.name);
2353
2351
  return sendResponse(response);
2354
2352
  }
@@ -2451,23 +2449,28 @@ export class OPCUAServer extends OPCUABaseServer {
2451
2449
  return sendError(StatusCodes.BadNothingToDo);
2452
2450
  }
2453
2451
 
2454
- const results: any[] = subscriptionIds.map((subscriptionId: number) => actionToPerform(session, subscriptionId));
2455
-
2456
- // resolve potential pending promises ....
2457
- for (let i = 0; i < results.length; i++) {
2458
- if (results[i].then) {
2459
- results[i] = await results[i];
2460
- }
2452
+ // check minimal
2453
+ if (
2454
+ request.subscriptionIds.length >
2455
+ Math.min(
2456
+ this.engine.serverCapabilities.maxSubscriptionsPerSession,
2457
+ this.engine.serverCapabilities.maxSubscriptions
2458
+ )
2459
+ ) {
2460
+ return sendError(StatusCodes.BadTooManyOperations);
2461
2461
  }
2462
2462
 
2463
+ const promises: Promise<T>[] = subscriptionIds.map((subscriptionId: number) =>
2464
+ actionToPerform(session, subscriptionId)
2465
+ );
2466
+ const results: T[] = await Promise.all(promises);
2467
+
2468
+ const serviceResult: StatusCode = StatusCodes.Good;
2463
2469
  const response = new ResponseClass({
2464
2470
  responseHeader: {
2465
- serviceResult:
2466
- request.subscriptionIds.length > this.engine.serverCapabilities.maxSubscriptionsPerSession
2467
- ? StatusCodes.BadTooManyOperations
2468
- : StatusCodes.Good
2471
+ serviceResult
2469
2472
  },
2470
- results
2473
+ results: results as any
2471
2474
  });
2472
2475
  sendResponse(response);
2473
2476
  }
@@ -853,8 +853,9 @@ export class OPCUAServerEndPoint extends EventEmitter implements ServerSecureCha
853
853
  } else {
854
854
  debugLog("OPCUAServerEndPoint#_registerChannel called when end point is shutdown !");
855
855
  debugLog(" -> channel will be forcefully terminated");
856
- channel.close();
857
- channel.dispose();
856
+ channel.close(() => {
857
+ channel.dispose();
858
+ });
858
859
  }
859
860
  }
860
861
 
@@ -927,17 +928,22 @@ export class OPCUAServerEndPoint extends EventEmitter implements ServerSecureCha
927
928
  errorLog(chalk.bgRed.white("PREVENTING DDOS ATTACK => maxConnection =" + this.maxConnections));
928
929
 
929
930
  const unused_channels: ServerSecureChannelLayer[] = this.getChannels().filter((channel1: ServerSecureChannelLayer) => {
930
- return !channel1.isOpened && !channel1.hasSession;
931
+ return !channel1.hasSession;
931
932
  });
932
933
  if (unused_channels.length === 0) {
934
+ doDebug && console.log(
935
+ this.getChannels()
936
+ .map(({ status, isOpened, hasSession }) => `${status} ${isOpened} ${hasSession}\n`)
937
+ .join(" ")
938
+ );
933
939
  // all channels are in used , we cannot get any
934
- errorLog("All channels are in used ! let cancel some");
940
+ errorLog(`All channels are in used ! we cannot cancel any ${this.getChannels().length}`);
935
941
  // istanbul ignore next
936
942
  if (doDebug) {
937
943
  console.log(" - all channels are used !!!!");
938
- dumpChannelInfo(this.getChannels());
944
+ false && dumpChannelInfo(this.getChannels());
939
945
  }
940
- setTimeout(deny_connection, 10);
946
+ setTimeout(deny_connection, 1000);
941
947
  return;
942
948
  }
943
949
  // istanbul ignore next
@@ -948,7 +954,7 @@ export class OPCUAServerEndPoint extends EventEmitter implements ServerSecureCha
948
954
  );
949
955
  }
950
956
  const channel = unused_channels[0];
951
- errorLog("Closing channel ", channel.hashKey);
957
+ errorLog(`${unused_channels.length} : Forcefully closing oldest channel that have no session: ${channel.hashKey}`);
952
958
  channel.close(() => {
953
959
  // istanbul ignore next
954
960
  if (doDebug) {
@@ -338,7 +338,7 @@ export interface ServerEngineOptions {
338
338
  export interface CreateSessionOption {
339
339
  clientDescription?: ApplicationDescription;
340
340
  sessionTimeout?: number;
341
- server: IServerBase;
341
+ server?: IServerBase;
342
342
  }
343
343
 
344
344
  export type ClosingReason = "Timeout" | "Terminated" | "CloseSession" | "Forcing";
@@ -374,7 +374,7 @@ export class ServerEngine extends EventEmitter implements IAddressSpaceAccessor
374
374
  private _serverStatus: ServerStatusDataType;
375
375
  private _globalCounter: { totalMonitoredItemCount: number } = { totalMonitoredItemCount: 0 };
376
376
 
377
- constructor(options: ServerEngineOptions) {
377
+ constructor(options?: ServerEngineOptions) {
378
378
  super();
379
379
 
380
380
  options = options || ({ applicationUri: "" } as ServerEngineOptions);
@@ -1423,7 +1423,7 @@ export class ServerEngine extends EventEmitter implements IAddressSpaceAccessor
1423
1423
  * @param [options.clientDescription] {ApplicationDescription}
1424
1424
  * @return {ServerSession}
1425
1425
  */
1426
- public createSession(options: CreateSessionOption): ServerSession {
1426
+ public createSession(options?: CreateSessionOption): ServerSession {
1427
1427
  options = options || {};
1428
1428
  options.server = options.server || {};
1429
1429
  debugLog("createSession : increasing serverDiagnosticsSummary cumulatedSessionCount/currentSessionCount ");
@@ -1645,9 +1645,10 @@ export class ServerEngine extends EventEmitter implements IAddressSpaceAccessor
1645
1645
  subscription.$session._unexposeSubscriptionDiagnostics(subscription);
1646
1646
  }
1647
1647
 
1648
- await ServerSidePublishEngine.transferSubscription(subscription, session.publishEngine, sendInitialValues);
1649
1648
  subscription.$session = session;
1650
-
1649
+
1650
+ await ServerSidePublishEngine.transferSubscription(subscription, session.publishEngine, sendInitialValues);
1651
+
1651
1652
  session._exposeSubscriptionDiagnostics(subscription);
1652
1653
 
1653
1654
  assert((subscription.publishEngine as any) === session.publishEngine);