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.
- package/.mocharc.yml +9 -9
- package/.nycrc.json +14 -14
- package/LICENSE +20 -20
- package/README.md +8 -8
- package/dist/client_session.d.ts +2 -2
- package/dist/opcua_client.d.ts +4 -0
- package/dist/opcua_client.js +14 -0
- package/dist/opcua_client.js.map +1 -1
- package/dist/private/client_base_impl.js +13 -2
- package/dist/private/client_base_impl.js.map +1 -1
- package/dist/private/client_publish_engine.js +14 -5
- package/dist/private/client_publish_engine.js.map +1 -1
- package/dist/private/client_session_impl.js +1 -3
- package/dist/private/client_session_impl.js.map +1 -1
- package/dist/private/opcua_client_impl.d.ts +33 -0
- package/dist/private/opcua_client_impl.js +83 -9
- package/dist/private/opcua_client_impl.js.map +1 -1
- package/dist/reconnection.js +31 -15
- package/dist/reconnection.js.map +1 -1
- package/dist/tools/read_history_server_capabilities.js +1 -1
- package/dist/tools/read_history_server_capabilities.js.map +1 -1
- package/package.json +41 -41
- package/source/client_monitored_item.ts +36 -36
- package/source/client_session.ts +2 -2
- package/source/index.ts +75 -75
- package/source/opcua_client.ts +13 -0
- package/source/private/client_base_impl.ts +13 -2
- package/source/private/client_monitored_item_base_impl.ts +3 -3
- package/source/private/client_publish_engine.ts +13 -6
- package/source/private/client_session_impl.ts +2 -4
- package/source/private/opcua_client_impl.ts +101 -10
- package/source/reconnection.ts +35 -17
- package/source/tools/read_history_server_capabilities.ts +1 -1
- package/test_helpers/create_certificates.js +1 -1
- 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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
package/source/reconnection.ts
CHANGED
|
@@ -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
|
-
|
|
168
|
-
|
|
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
|
|
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
|
-
|
|
390
|
-
|
|
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
|
-
|
|
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
|
|
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
|
+
|