node-opcua-client 2.56.3 → 2.60.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 (35) hide show
  1. package/.mocharc.yml +9 -9
  2. package/.nycrc.json +14 -14
  3. package/LICENSE +20 -20
  4. package/README.md +8 -8
  5. package/dist/client_session.d.ts +2 -2
  6. package/dist/opcua_client.d.ts +4 -0
  7. package/dist/opcua_client.js +14 -0
  8. package/dist/opcua_client.js.map +1 -1
  9. package/dist/private/client_base_impl.js +13 -2
  10. package/dist/private/client_base_impl.js.map +1 -1
  11. package/dist/private/client_publish_engine.js +14 -5
  12. package/dist/private/client_publish_engine.js.map +1 -1
  13. package/dist/private/client_session_impl.js +1 -3
  14. package/dist/private/client_session_impl.js.map +1 -1
  15. package/dist/private/opcua_client_impl.d.ts +33 -0
  16. package/dist/private/opcua_client_impl.js +83 -9
  17. package/dist/private/opcua_client_impl.js.map +1 -1
  18. package/dist/reconnection.js +31 -15
  19. package/dist/reconnection.js.map +1 -1
  20. package/dist/tools/read_history_server_capabilities.js +1 -1
  21. package/dist/tools/read_history_server_capabilities.js.map +1 -1
  22. package/package.json +41 -41
  23. package/source/client_monitored_item.ts +36 -36
  24. package/source/client_session.ts +2 -2
  25. package/source/index.ts +75 -75
  26. package/source/opcua_client.ts +13 -0
  27. package/source/private/client_base_impl.ts +13 -2
  28. package/source/private/client_monitored_item_base_impl.ts +3 -3
  29. package/source/private/client_publish_engine.ts +13 -6
  30. package/source/private/client_session_impl.ts +2 -4
  31. package/source/private/opcua_client_impl.ts +101 -10
  32. package/source/reconnection.ts +35 -17
  33. package/source/tools/read_history_server_capabilities.ts +1 -1
  34. package/test_helpers/create_certificates.js +1 -1
  35. package/typedoc.js +20 -20
@@ -418,6 +418,42 @@ export class OPCUAClientImpl extends ClientBaseImpl implements OPCUAClient {
418
418
  });
419
419
  }
420
420
 
421
+ /**
422
+ * createSession2 create a session with persistance
423
+ *
424
+ * - if the server returns BadTooManySession, the method will make an other attempt
425
+ * unitl create session succeed or connection is closed.
426
+ *
427
+ * @experiemental
428
+ * @param userIdentityInfo
429
+ */
430
+ public async createSession2(userIdentityInfo?: UserIdentityInfo): Promise<ClientSession>;
431
+ public createSession2(userIdentityInfo: UserIdentityInfo, callback: Callback<ClientSession>): void;
432
+ public createSession2(callback: Callback<ClientSession>): void;
433
+ public createSession2(...args: any[]): any {
434
+ if (args.length === 1) {
435
+ return this.createSession2({ type: UserTokenType.Anonymous }, args[0]);
436
+ }
437
+ const userIdentityInfo = args[0] as UserIdentityInfo;
438
+ const callback = args[1] as Callback<ClientSession>;
439
+ if (!this._secureChannel) {
440
+ // we do not have a connection anymore
441
+ return callback(new Error("Connection is closed"));
442
+ }
443
+ return this.createSession(args[0], (err: Error | null, session?: ClientSession) => {
444
+ if (err && err.message.match(/BadTooManySessions/)) {
445
+ const delayToRetry = 5; // seconds
446
+ errorLog(`TooManySession .... we need to retry later ... in ${delayToRetry} secondes`);
447
+ const retryCreateSessionTimer = setTimeout(() => {
448
+ errorLog("TooManySession .... now retrying");
449
+ this.createSession2(userIdentityInfo, callback);
450
+ }, delayToRetry * 1000);
451
+ return;
452
+ }
453
+ callback(err, session);
454
+ });
455
+ }
456
+
421
457
  /**
422
458
  * @method changeSessionIdentity
423
459
  * @param session
@@ -471,6 +507,52 @@ export class OPCUAClientImpl extends ClientBaseImpl implements OPCUAClient {
471
507
  return str;
472
508
  }
473
509
 
510
+ /**
511
+ *
512
+ * @example
513
+ *
514
+ * ```javascript
515
+ *
516
+ * const session = await OPCUAClient.createSession(endpointUrl);
517
+ * const dataValue = await session.read({ nodeId, attributeId: AttributeIds.Value });
518
+ * await session.close();
519
+ *
520
+ * ```
521
+ * @stability experimental
522
+ *
523
+ * @param endpointUrl
524
+ * @param userIdentity
525
+ * @returns session
526
+ *
527
+ *
528
+ * const create
529
+ */
530
+ public static async createSession(
531
+ endpointUrl: string,
532
+ userIdentity?: UserIdentityInfo,
533
+ clientOptions?: OPCUAClientOptions
534
+ ): Promise<ClientSession> {
535
+ const client = OPCUAClient.create(clientOptions || {});
536
+
537
+ await client.connect(endpointUrl);
538
+ const session = await client.createSession2(userIdentity);
539
+
540
+ const oldClose = session.close as any;
541
+ (session as any).close = thenify.withCallback((...args: any[]): any => {
542
+ if (args.length === 1) {
543
+ return session.close(true, args[0]);
544
+ }
545
+ const deleteSubscriptions = args[0] as boolean;
546
+ const callback = args[1] as Callback<void>;
547
+ session.close = oldClose;
548
+ oldClose.call(session, deleteSubscriptions, (err?: Error) => {
549
+ client.disconnect((err?: Error | null) => {
550
+ callback(err!);
551
+ });
552
+ });
553
+ });
554
+ return session;
555
+ }
474
556
  /**
475
557
  * @method withSession
476
558
  */
@@ -484,7 +566,6 @@ export class OPCUAClientImpl extends ClientBaseImpl implements OPCUAClient {
484
566
  inner_func: (session: ClientSession, done: (err?: Error) => void) => void,
485
567
  callback: (err?: Error) => void
486
568
  ): void;
487
-
488
569
  /**
489
570
  * @internal
490
571
  * @param args
@@ -522,7 +603,7 @@ export class OPCUAClientImpl extends ClientBaseImpl implements OPCUAClient {
522
603
 
523
604
  // step 2 : createSession
524
605
  (innerCallback: ErrorCallback) => {
525
- this.createSession(userIdentity, (err: Error | null, session?: ClientSession) => {
606
+ this.createSession2(userIdentity, (err: Error | null, session?: ClientSession) => {
526
607
  if (err) {
527
608
  return innerCallback(err);
528
609
  }
@@ -570,7 +651,7 @@ export class OPCUAClientImpl extends ClientBaseImpl implements OPCUAClient {
570
651
  ],
571
652
  (err1) => {
572
653
  if (err1) {
573
- console.log("err", err1);
654
+ console.log("err", err1.message);
574
655
  }
575
656
  if (need_disconnect) {
576
657
  errorLog("Disconnecting client after failure");
@@ -624,7 +705,7 @@ export class OPCUAClientImpl extends ClientBaseImpl implements OPCUAClient {
624
705
  typeof connectionPoint === "string" ? { type: UserTokenType.Anonymous } : connectionPoint.userIdentity;
625
706
 
626
707
  await this.connect(endpointUrl);
627
- const session = await this.createSession(userIdentity);
708
+ const session = await this.createSession2(userIdentity);
628
709
 
629
710
  let result;
630
711
  try {
@@ -673,7 +754,7 @@ export class OPCUAClientImpl extends ClientBaseImpl implements OPCUAClient {
673
754
 
674
755
  assert(typeof callback === "function");
675
756
  if (!this._secureChannel) {
676
- throw new Error(" client must be connected first");
757
+ return callback!(new Error(" client must be connected first"));
677
758
  }
678
759
  // istanbul ignore next
679
760
  if (!this.__resolveEndPoint() || !this.endpoint) {
@@ -737,13 +818,15 @@ export class OPCUAClientImpl extends ClientBaseImpl implements OPCUAClient {
737
818
  callback: (err: Error | null, session?: ClientSessionImpl) => void
738
819
  ): void {
739
820
  assert(typeof callback === "function");
740
- if (!this._secureChannel) {
741
- throw new Error("Invalid channel");
742
- }
743
821
  assert(this.serverUri !== undefined, " must have a valid server URI " + this.serverUri);
744
822
  assert(this.endpointUrl !== undefined, " must have a valid server endpointUrl");
745
823
  assert(this.endpoint);
746
824
 
825
+ // istanbul ignore next
826
+ if (!this._secureChannel) {
827
+ return callback(new Error("Invalid channel"));
828
+ }
829
+
747
830
  const applicationUri = this._getApplicationUri();
748
831
 
749
832
  const applicationDescription: ApplicationDescriptionOptions = {
@@ -777,7 +860,13 @@ export class OPCUAClientImpl extends ClientBaseImpl implements OPCUAClient {
777
860
  this.performMessageTransaction(request, (err: Error | null, response?: Response) => {
778
861
  /* istanbul ignore next */
779
862
  if (err) {
780
- return callback(err);
863
+ // we could have an invalid state here or a connection error
864
+ errorLog("error: ", err.message, " retrying in ... 5 secondes");
865
+ setTimeout(() => {
866
+ errorLog(" .... now retrying");
867
+ this.__createSession_step3(session, callback);
868
+ }, 5 * 1000);
869
+ return;
781
870
  }
782
871
 
783
872
  /* istanbul ignore next */
@@ -786,7 +875,7 @@ export class OPCUAClientImpl extends ClientBaseImpl implements OPCUAClient {
786
875
  }
787
876
 
788
877
  if (response.responseHeader.serviceResult === StatusCodes.BadTooManySessions) {
789
- return callback(new Error("Too Many Sessions : " + response.responseHeader.serviceResult.toString()));
878
+ return callback(new Error(response.responseHeader.serviceResult.toString()));
790
879
  }
791
880
 
792
881
  if (response.responseHeader.serviceResult !== StatusCodes.Good) {
@@ -848,6 +937,7 @@ export class OPCUAClientImpl extends ClientBaseImpl implements OPCUAClient {
848
937
  public _activateSession(session: ClientSessionImpl, callback: (err: Error | null, session?: ClientSessionImpl) => void): void {
849
938
  // see OPCUA Part 4 - $7.35
850
939
  assert(typeof callback === "function");
940
+
851
941
  // istanbul ignore next
852
942
  if (!this._secureChannel) {
853
943
  return callback(new Error(" No secure channel"));
@@ -1169,6 +1259,7 @@ const thenify = require("thenify");
1169
1259
  *
1170
1260
  */
1171
1261
  OPCUAClientImpl.prototype.createSession = thenify.withCallback(OPCUAClientImpl.prototype.createSession);
1262
+ OPCUAClientImpl.prototype.createSession2 = thenify.withCallback(OPCUAClientImpl.prototype.createSession2);
1172
1263
  /**
1173
1264
  * @method changeSessionIdentity
1174
1265
  * @async
@@ -8,7 +8,7 @@ import * as chalk from "chalk";
8
8
  import { assert } from "node-opcua-assert";
9
9
  import { checkDebugFlag, make_debugLog, make_errorLog, make_warningLog } from "node-opcua-debug";
10
10
  import { TransferSubscriptionsRequest, TransferSubscriptionsResponse } from "node-opcua-service-subscription";
11
- import { StatusCodes } from "node-opcua-status-code";
11
+ import { CallbackT, StatusCodes } from "node-opcua-status-code";
12
12
  import { ErrorCallback } from "node-opcua-status-code";
13
13
 
14
14
  import { SubscriptionId } from "./client_session";
@@ -133,6 +133,33 @@ function _ask_for_subscription_republish(session: ClientSessionImpl, callback: (
133
133
  });
134
134
  }
135
135
 
136
+ function create_session_and_repeat_if_failed(
137
+ client: IClientBase,
138
+ session: ClientSessionImpl,
139
+ callback: CallbackT<ClientSessionImpl>
140
+ ) {
141
+ if (session.hasBeenClosed()) {
142
+ return callback(new Error("Cannot complete subscription republish due to session termination"));
143
+ }
144
+ debugLog(chalk.bgWhite.red(" => creating a new session ...."));
145
+ // create new session, based on old session,
146
+ // so we can reuse subscriptions data
147
+ client.__createSession_step2(session, (err: Error | null, session1?: ClientSessionImpl) => {
148
+ debugLog(chalk.bgWhite.cyan(" => creating a new session (based on old session data).... Done"));
149
+ if (!err && session1) {
150
+ const newSession = session1;
151
+ assert(session === session1, "session should have been recycled");
152
+ callback(err, newSession);
153
+ return;
154
+ } else {
155
+ setTimeout(() => {
156
+ create_session_and_repeat_if_failed(client, session, callback);
157
+ }, 1000);
158
+ return;
159
+ }
160
+ callback(err);
161
+ });
162
+ }
136
163
  function repair_client_session_by_recreating_a_new_session(
137
164
  client: IClientBase,
138
165
  session: ClientSessionImpl,
@@ -164,20 +191,11 @@ function repair_client_session_by_recreating_a_new_session(
164
191
  },
165
192
 
166
193
  function create_new_session(innerCallback: ErrorCallback) {
167
- if (session.hasBeenClosed()) {
168
- return innerCallback(new Error("Cannot complete subscription republish due to session termination"));
169
- }
170
-
171
- debugLog(chalk.bgWhite.red(" => creating a new session ...."));
172
- // create new session, based on old session,
173
- // so we can reuse subscriptions data
174
- client.__createSession_step2(session, (err: Error | null, session1?: ClientSessionImpl) => {
175
- debugLog(chalk.bgWhite.cyan(" => creating a new session (based on old session data).... Done"));
176
- if (!err && session1) {
177
- newSession = session1;
178
- assert(session === session1, "session should have been recycled");
194
+ create_session_and_repeat_if_failed(client, session, (err?: Error | null, _newSession?: ClientSessionImpl) => {
195
+ if (_newSession) {
196
+ newSession = _newSession;
179
197
  }
180
- innerCallback(err ? err : undefined);
198
+ innerCallback(err || undefined);
181
199
  });
182
200
  },
183
201
 
@@ -386,8 +404,8 @@ export function repair_client_session(client: IClientBase, session: ClientSessio
386
404
  _repair_client_session(client, session, (err) => {
387
405
  privateSession._reconnecting.reconnecting = false;
388
406
  if (err) {
389
- debugLog(chalk.red("SESSION RESTORED HAS FAILED! retrying"), err.message, session.sessionId.toString());
390
- // xx return repair_client_session(client, session, callback);
407
+ errorLog(chalk.red("SESSION RESTORED HAS FAILED! retrying"), err.message, session.sessionId.toString());
408
+ return _repair_client_session(client, session, callback);
391
409
  }
392
410
  debugLog(chalk.yellow("SESSION RESTORED"), session.sessionId.toString());
393
411
  session.emit("session_restored");
@@ -413,7 +431,7 @@ export function repair_client_sessions(client: IClientBase, callback: (err?: Err
413
431
  repair_client_session(client, session as ClientSessionImpl, next);
414
432
  },
415
433
  (err) => {
416
- debugLog(chalk.red.bgWhite("sessions reactivation completed: err ", err ? err.message : "null"));
434
+ err && errorLog("sessions reactivation completed: err ", err ? err.message : "null");
417
435
  return callback(err!);
418
436
  }
419
437
  );
@@ -91,7 +91,7 @@ export function readHistoryServerCapabilities(
91
91
  const nodeIds = results.map((innerResult: BrowsePathResult) =>
92
92
  innerResult.statusCode === StatusCodes.Good && innerResult.targets
93
93
  ? innerResult.targets[0].targetId
94
- : NodeId.nullNodeId
94
+ : new NodeId()
95
95
  );
96
96
 
97
97
  const nodesToRead: ReadValueIdOptions[] = nodeIds.map((nodeId: NodeId) => ({
@@ -1 +1 @@
1
- require("node-opcua-pki/bin/crypto_create_CA");
1
+ require("node-opcua-pki/bin/crypto_create_CA");
package/typedoc.js CHANGED
@@ -1,20 +1,20 @@
1
- // (e.g. typedoc --options ./typedoc.js ./src), then you can set an array of exclude paths in the configuration:
2
- // typedoc.js:
3
-
4
- module.exports = {
5
- src: [ "./source/index.ts" ],
6
- out: "./out1/",
7
-
8
- readme: "./documentation/readme.md",
9
- includes: "./documentation",
10
- exclude: ["*/@types/**"],
11
- mode: "file",
12
- //xx excludeExternals: true,
13
- excludeNotExported: true,
14
- excludePrivate: true,
15
- includeDeclarations: true,
16
- moduleResolution: "node",
17
- theme: "default",
18
- logger: "console"
19
- };
20
-
1
+ // (e.g. typedoc --options ./typedoc.js ./src), then you can set an array of exclude paths in the configuration:
2
+ // typedoc.js:
3
+
4
+ module.exports = {
5
+ src: [ "./source/index.ts" ],
6
+ out: "./out1/",
7
+
8
+ readme: "./documentation/readme.md",
9
+ includes: "./documentation",
10
+ exclude: ["*/@types/**"],
11
+ mode: "file",
12
+ //xx excludeExternals: true,
13
+ excludeNotExported: true,
14
+ excludePrivate: true,
15
+ includeDeclarations: true,
16
+ moduleResolution: "node",
17
+ theme: "default",
18
+ logger: "console"
19
+ };
20
+