node-opcua-client 2.59.0 → 2.62.1

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.
@@ -278,7 +278,7 @@ function createUserNameIdentityToken(
278
278
 
279
279
  // istanbul ignore next
280
280
  if (!cryptoFactory) {
281
- throw new Error(" Unsupported security Policy");
281
+ throw new Error(" Unsupported security Policy "+ securityPolicy.toString());
282
282
  }
283
283
 
284
284
  identityToken = new UserNameIdentityToken({
@@ -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
@@ -514,6 +595,7 @@ export class OPCUAClientImpl extends ClientBaseImpl implements OPCUAClient {
514
595
  this.connect(endpointUrl, (err?: Error) => {
515
596
  need_disconnect = true;
516
597
  if (err) {
598
+ warningLog(err.message);
517
599
  errorLog(" cannot connect to endpoint :", endpointUrl);
518
600
  }
519
601
  innerCallback(err);
@@ -522,7 +604,7 @@ export class OPCUAClientImpl extends ClientBaseImpl implements OPCUAClient {
522
604
 
523
605
  // step 2 : createSession
524
606
  (innerCallback: ErrorCallback) => {
525
- this.createSession(userIdentity, (err: Error | null, session?: ClientSession) => {
607
+ this.createSession2(userIdentity, (err: Error | null, session?: ClientSession) => {
526
608
  if (err) {
527
609
  return innerCallback(err);
528
610
  }
@@ -570,7 +652,7 @@ export class OPCUAClientImpl extends ClientBaseImpl implements OPCUAClient {
570
652
  ],
571
653
  (err1) => {
572
654
  if (err1) {
573
- console.log("err", err1);
655
+ console.log("err", err1.message);
574
656
  }
575
657
  if (need_disconnect) {
576
658
  errorLog("Disconnecting client after failure");
@@ -624,7 +706,7 @@ export class OPCUAClientImpl extends ClientBaseImpl implements OPCUAClient {
624
706
  typeof connectionPoint === "string" ? { type: UserTokenType.Anonymous } : connectionPoint.userIdentity;
625
707
 
626
708
  await this.connect(endpointUrl);
627
- const session = await this.createSession(userIdentity);
709
+ const session = await this.createSession2(userIdentity);
628
710
 
629
711
  let result;
630
712
  try {
@@ -673,7 +755,7 @@ export class OPCUAClientImpl extends ClientBaseImpl implements OPCUAClient {
673
755
 
674
756
  assert(typeof callback === "function");
675
757
  if (!this._secureChannel) {
676
- throw new Error(" client must be connected first");
758
+ return callback!(new Error(" client must be connected first"));
677
759
  }
678
760
  // istanbul ignore next
679
761
  if (!this.__resolveEndPoint() || !this.endpoint) {
@@ -737,13 +819,15 @@ export class OPCUAClientImpl extends ClientBaseImpl implements OPCUAClient {
737
819
  callback: (err: Error | null, session?: ClientSessionImpl) => void
738
820
  ): void {
739
821
  assert(typeof callback === "function");
740
- if (!this._secureChannel) {
741
- throw new Error("Invalid channel");
742
- }
743
822
  assert(this.serverUri !== undefined, " must have a valid server URI " + this.serverUri);
744
823
  assert(this.endpointUrl !== undefined, " must have a valid server endpointUrl");
745
824
  assert(this.endpoint);
746
825
 
826
+ // istanbul ignore next
827
+ if (!this._secureChannel) {
828
+ return callback(new Error("Invalid channel"));
829
+ }
830
+
747
831
  const applicationUri = this._getApplicationUri();
748
832
 
749
833
  const applicationDescription: ApplicationDescriptionOptions = {
@@ -777,7 +861,13 @@ export class OPCUAClientImpl extends ClientBaseImpl implements OPCUAClient {
777
861
  this.performMessageTransaction(request, (err: Error | null, response?: Response) => {
778
862
  /* istanbul ignore next */
779
863
  if (err) {
780
- return callback(err);
864
+ // we could have an invalid state here or a connection error
865
+ errorLog("error: ", err.message, " retrying in ... 5 secondes");
866
+ setTimeout(() => {
867
+ errorLog(" .... now retrying");
868
+ this.__createSession_step3(session, callback);
869
+ }, 5 * 1000);
870
+ return;
781
871
  }
782
872
 
783
873
  /* istanbul ignore next */
@@ -786,7 +876,7 @@ export class OPCUAClientImpl extends ClientBaseImpl implements OPCUAClient {
786
876
  }
787
877
 
788
878
  if (response.responseHeader.serviceResult === StatusCodes.BadTooManySessions) {
789
- return callback(new Error("Too Many Sessions : " + response.responseHeader.serviceResult.toString()));
879
+ return callback(new Error(response.responseHeader.serviceResult.toString()));
790
880
  }
791
881
 
792
882
  if (response.responseHeader.serviceResult !== StatusCodes.Good) {
@@ -848,6 +938,7 @@ export class OPCUAClientImpl extends ClientBaseImpl implements OPCUAClient {
848
938
  public _activateSession(session: ClientSessionImpl, callback: (err: Error | null, session?: ClientSessionImpl) => void): void {
849
939
  // see OPCUA Part 4 - $7.35
850
940
  assert(typeof callback === "function");
941
+
851
942
  // istanbul ignore next
852
943
  if (!this._secureChannel) {
853
944
  return callback(new Error(" No secure channel"));
@@ -1169,6 +1260,7 @@ const thenify = require("thenify");
1169
1260
  *
1170
1261
  */
1171
1262
  OPCUAClientImpl.prototype.createSession = thenify.withCallback(OPCUAClientImpl.prototype.createSession);
1263
+ OPCUAClientImpl.prototype.createSession2 = thenify.withCallback(OPCUAClientImpl.prototype.createSession2);
1172
1264
  /**
1173
1265
  * @method changeSessionIdentity
1174
1266
  * @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) => ({