node-opcua-client 2.101.0 → 2.103.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.
@@ -102,6 +102,10 @@ export interface OPCUAClientBaseOptions {
102
102
  * @default false
103
103
  */
104
104
  keepSessionAlive?: boolean;
105
+ /**
106
+ * the number of milliseconds that the client should wait until it sends a keep alive message to the server.
107
+ */
108
+ keepAliveInterval?: number;
105
109
 
106
110
  /**
107
111
  * certificate Manager
@@ -132,6 +136,7 @@ export interface OPCUAClientBaseOptions {
132
136
  * @advanced
133
137
  */
134
138
  transportSettings?: TransportSettings;
139
+ transportTimeout?: number;
135
140
  }
136
141
 
137
142
  export interface GetEndpointsOptions {
@@ -325,7 +330,6 @@ export interface OPCUAClientBase {
325
330
  readonly connectionStrategy: ConnectionStrategy;
326
331
  readonly keepPendingSessionsOnDisconnect: boolean;
327
332
  readonly endpointUrl: string;
328
- readonly keepSessionAlive: boolean;
329
333
  readonly applicationName: string;
330
334
  }
331
335
 
@@ -7,8 +7,10 @@ import { assert } from "node-opcua-assert";
7
7
  import { ServerState } from "node-opcua-common";
8
8
  import { VariableIds } from "node-opcua-constants";
9
9
  import { DataValue } from "node-opcua-data-value";
10
+ import { AttributeIds } from "node-opcua-basic-types";
10
11
  import { checkDebugFlag, make_debugLog, make_warningLog } from "node-opcua-debug";
11
12
  import { coerceNodeId } from "node-opcua-nodeid";
13
+ import { ClientSecureChannelLayer } from "node-opcua-secure-channel";
12
14
  import { StatusCodes } from "node-opcua-status-code";
13
15
  import { ClientSessionImpl } from "./private/client_session_impl";
14
16
 
@@ -28,9 +30,9 @@ export class ClientSessionKeepAliveManager extends EventEmitter implements Clien
28
30
  private timerId?: NodeJS.Timer;
29
31
  private pingTimeout: number;
30
32
  private lastKnownState?: ServerState;
31
- private checkInterval: number;
32
33
  private transactionInProgress = false;
33
34
  public count = 0;
35
+ public checkInterval: number;
34
36
 
35
37
  constructor(session: ClientSessionImpl) {
36
38
  super();
@@ -41,7 +43,7 @@ export class ClientSessionKeepAliveManager extends EventEmitter implements Clien
41
43
  this.count = 0;
42
44
  }
43
45
 
44
- public start(): void {
46
+ public start(keepAliveInterval?: number): void {
45
47
  assert(!this.timerId);
46
48
  /* istanbul ignore next*/
47
49
  if (this.session.timeout < 600) {
@@ -56,8 +58,12 @@ export class ClientSessionKeepAliveManager extends EventEmitter implements Clien
56
58
  );
57
59
  }
58
60
 
59
- this.pingTimeout = Math.min(this.session.timeout / 3, 20000);
60
- this.checkInterval = Math.max(50, Math.min((this.session.timeout * 2) / 3, 20000));
61
+ const selectedCheckInterval =
62
+ keepAliveInterval ||
63
+ Math.min(Math.floor(Math.min((this.session.timeout * 2) / 3, 20000)), ClientSecureChannelLayer.defaultTransportTimeout);
64
+
65
+ this.checkInterval = selectedCheckInterval;
66
+ this.pingTimeout = Math.floor(Math.min(Math.max(50, selectedCheckInterval / 2), 20000));
61
67
 
62
68
  // make sure first one is almost immediate
63
69
  this.timerId = setTimeout(() => this.ping_server(), this.pingTimeout);
@@ -103,7 +109,7 @@ export class ClientSessionKeepAliveManager extends EventEmitter implements Clien
103
109
  if (!this.timerId) {
104
110
  return 0; // keep-alive has been canceled ....
105
111
  }
106
- const now = Date.now();
112
+ const now = Date.now();
107
113
 
108
114
  const timeSinceLastServerContact = now - session.lastResponseReceivedTime.getTime();
109
115
  if (timeSinceLastServerContact < this.pingTimeout) {
@@ -141,41 +147,50 @@ export class ClientSessionKeepAliveManager extends EventEmitter implements Clien
141
147
  // Server_ServerStatus_State
142
148
 
143
149
  return new Promise((resolve) => {
144
- session.readVariableValue(serverStatusStateNodeId, (err: Error | null, dataValue?: DataValue) => {
145
- this.transactionInProgress = false;
146
-
147
- if (err || !dataValue || !dataValue.value) {
148
- if (err) {
149
- warningLog(chalk.cyan(" warning : ClientSessionKeepAliveManager#ping_server "), chalk.yellow(err.message));
150
+ session.read(
151
+ {
152
+ nodeId: serverStatusStateNodeId,
153
+ attributeId: AttributeIds.Value
154
+ },
155
+ (err: Error | null, dataValue?: DataValue) => {
156
+ this.transactionInProgress = false;
157
+
158
+ if (err || !dataValue || !dataValue.value) {
159
+ if (err) {
160
+ warningLog(
161
+ chalk.cyan(" warning : ClientSessionKeepAliveManager#ping_server "),
162
+ chalk.yellow(err.message)
163
+ );
164
+ }
165
+ /**
166
+ * @event failure
167
+ * raised when the server is not responding or is responding with en error to
168
+ * the keep alive read Variable value transaction
169
+ */
170
+ this.emit("failure");
171
+ resolve(0);
172
+ return;
150
173
  }
151
- /**
152
- * @event failure
153
- * raised when the server is not responding or is responding with en error to
154
- * the keep alive read Variable value transaction
155
- */
156
- this.emit("failure");
157
- resolve(0);
158
- return;
159
- }
160
174
 
161
- if (dataValue.statusCode.isGood()) {
162
- const newState = dataValue.value.value as ServerState;
163
- // istanbul ignore next
164
- if (newState !== this.lastKnownState && this.lastKnownState) {
165
- warningLog(
166
- "ClientSessionKeepAliveManager#Server state has changed = ",
167
- ServerState[newState],
168
- " was ",
169
- ServerState[this.lastKnownState]
170
- );
175
+ if (dataValue.statusCode.isGood()) {
176
+ const newState = dataValue.value.value as ServerState;
177
+ // istanbul ignore next
178
+ if (newState !== this.lastKnownState && this.lastKnownState) {
179
+ warningLog(
180
+ "ClientSessionKeepAliveManager#Server state has changed = ",
181
+ ServerState[newState],
182
+ " was ",
183
+ ServerState[this.lastKnownState]
184
+ );
185
+ }
186
+ this.lastKnownState = newState;
187
+ this.count++; // increase successful counter
171
188
  }
172
- this.lastKnownState = newState;
173
- this.count++; // increase successful counter
189
+ debugLog("emit keepalive");
190
+ this.emit("keepalive", this.lastKnownState, this.count);
191
+ resolve(0);
174
192
  }
175
- debugLog("emit keepalive");
176
- this.emit("keepalive", this.lastKnownState, this.count);
177
- resolve(0);
178
- });
193
+ );
179
194
  });
180
195
  }
181
196
  }
@@ -16,7 +16,6 @@ import { ClientSubscription, ClientSubscriptionOptions } from "./client_subscrip
16
16
  import { OPCUAClientImpl } from "./private/opcua_client_impl";
17
17
  import { UserIdentityInfo } from "./user_identity_info";
18
18
 
19
-
20
19
  export interface OPCUAClientOptions extends OPCUAClientBaseOptions {
21
20
  /**
22
21
  * the requested session timeout in CreateSession (ms)
@@ -43,51 +42,6 @@ export interface OPCUAClientOptions extends OPCUAClientBaseOptions {
43
42
  * @default true
44
43
  */
45
44
  endpointMustExist?: boolean;
46
-
47
- // --------------------------------------------------------------------
48
- connectionStrategy?: ConnectionStrategyOptions;
49
-
50
- /** the server certificate. */
51
- serverCertificate?: Certificate;
52
-
53
- /***
54
- * default secure token lifetime in ms
55
- */
56
- defaultSecureTokenLifetime?: number;
57
-
58
- /**
59
- * the security mode
60
- * @default MessageSecurityMode.None
61
- */
62
- securityMode?: MessageSecurityMode | string;
63
-
64
- /**
65
- * the security policy
66
- * @default SecurityPolicy.None
67
- */
68
- securityPolicy?: SecurityPolicy | string;
69
-
70
- /**
71
- * @default false
72
- */
73
- keepSessionAlive?: boolean;
74
-
75
- /**
76
- * client certificate pem file.
77
- * @default "certificates/client_self-signed_cert_2048.pem"
78
- */
79
- certificateFile?: string;
80
-
81
- /**
82
- * client private key pem file.
83
- * @default "certificates/client_key_2048.pem"
84
- */
85
- privateKeyFile?: string;
86
-
87
- /**
88
- * a client name string that will be used to generate session names.
89
- */
90
- clientName?: string;
91
45
  }
92
46
 
93
47
  export interface OPCUAClient extends OPCUAClientBase {
@@ -365,6 +365,7 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
365
365
  * true if session shall periodically probe the server to keep the session alive and prevent timeout
366
366
  */
367
367
  public keepSessionAlive: boolean;
368
+ public readonly keepAliveInterval?: number;
368
369
 
369
370
  public _sessions: ClientSessionImpl[];
370
371
  protected _serverEndpoints: EndpointDescription[];
@@ -383,6 +384,7 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
383
384
  private _tmpClient?: OPCUAClientBase;
384
385
  private _instanceNumber: number;
385
386
  private _transportSettings: TransportSettings;
387
+ private _transportTimeout?: number;
386
388
 
387
389
  public clientCertificateManager: OPCUACertificateManager;
388
390
 
@@ -451,6 +453,7 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
451
453
  this.serverCertificate = options.serverCertificate;
452
454
 
453
455
  this.keepSessionAlive = typeof options.keepSessionAlive === "boolean" ? options.keepSessionAlive : false;
456
+ this.keepAliveInterval = options.keepAliveInterval;
454
457
 
455
458
  // statistics...
456
459
  this._byteRead = 0;
@@ -471,6 +474,7 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
471
474
  this._setInternalState("disconnected");
472
475
 
473
476
  this._transportSettings = options.transportSettings || {};
477
+ this._transportTimeout = options.transportTimeout;
474
478
  }
475
479
 
476
480
  private _cancel_reconnection(callback: ErrorCallback) {
@@ -645,8 +649,8 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
645
649
  securityPolicy: this.securityPolicy,
646
650
  serverCertificate: this.serverCertificate,
647
651
  tokenRenewalInterval: this.tokenRenewalInterval,
648
- transportSettings: this._transportSettings
649
- // transportTimeout:
652
+ transportSettings: this._transportSettings,
653
+ transportTimeout: this._transportTimeout
650
654
  });
651
655
  secureChannel.on("backoff", (count: number, delay: number) => {
652
656
  this.emit("backoff", count, delay);
@@ -1288,8 +1292,8 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
1288
1292
  setImmediate(callback);
1289
1293
  });
1290
1294
  } else {
1291
- this.emit("close", null);
1292
1295
  this._setInternalState("disconnected");
1296
+ // this.emit("close", null);
1293
1297
  setImmediate(callback);
1294
1298
  }
1295
1299
  }
@@ -1569,12 +1573,14 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
1569
1573
  * @event close
1570
1574
  * @param error
1571
1575
  */
1572
- this.emit("close", err);
1576
+ if (err) {
1577
+ this.emit("connection_lost", err?.message); // instead of "close"
1578
+ }
1579
+ this.emit("close", err); // instead of "close"
1573
1580
  } else {
1574
1581
  /**
1575
1582
  * @event connection_lost
1576
1583
  */
1577
- // this.emit("close", err);
1578
1584
  if (this.reconnectOnFailure && this._internalState !== "reconnecting") {
1579
1585
  debugLog(" ClientBaseImpl emitting connection_lost");
1580
1586
  this.emit("connection_lost", err?.message); // instead of "close"
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * @module node-opcua-client-private
3
3
  */
4
+ import { types } from "util";
4
5
  import * as async from "async";
5
6
  import * as chalk from "chalk";
6
7
  import { assert } from "node-opcua-assert";
@@ -471,7 +472,7 @@ export class ClientSidePublishEngine {
471
472
  debugLog("__repairSubscription for SubscriptionId ", subscriptionId);
472
473
 
473
474
  this._republish(subscription, subscriptionId, (err?: Error) => {
474
- assert(!err || err instanceof Error);
475
+ assert(!err || types.isNativeError(err));
475
476
 
476
477
  debugLog("__repairSubscription--------------------- err =", err ? err.message : null);
477
478
 
@@ -23,7 +23,7 @@ import { ExtensionObject } from "node-opcua-extension-object";
23
23
  import { coerceNodeId, NodeId, NodeIdLike, resolveNodeId } from "node-opcua-nodeid";
24
24
  import { getBuiltInDataType, getArgumentDefinitionHelper, IBasicSession, IBasicTransportSettings } from "node-opcua-pseudo-session";
25
25
  import { AnyConstructorFunc } from "node-opcua-schemas";
26
- import { requestHandleNotSetValue, SignatureData } from "node-opcua-secure-channel";
26
+ import { ClientSecureChannelLayer, requestHandleNotSetValue, SignatureData } from "node-opcua-secure-channel";
27
27
  import { BrowseDescription, BrowseRequest, BrowseResponse, BrowseResult } from "node-opcua-service-browse";
28
28
  import { CallMethodRequest, CallMethodResult, CallRequest, CallResponse } from "node-opcua-service-call";
29
29
  import { EndpointDescription } from "node-opcua-service-endpoints";
@@ -309,7 +309,7 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession {
309
309
  get subscriptionCount(): number {
310
310
  return this._publishEngine ? this._publishEngine.subscriptionCount : 0;
311
311
  }
312
-
312
+
313
313
  get isReconnecting(): boolean {
314
314
  return this._client ? this._client.isReconnecting || this._reconnecting?.reconnecting : false;
315
315
  }
@@ -546,13 +546,12 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession {
546
546
  * ```javascript
547
547
  * const dataValues = await session.readVariableValue(["ns=1;s=Temperature","ns=1;s=Pressure"]);
548
548
  * ```
549
+ *
550
+ * @deprecated
549
551
  */
550
552
  public readVariableValue(nodeId: NodeIdLike, callback: ResponseCallback<DataValue>): void;
551
-
552
553
  public readVariableValue(nodeIds: NodeIdLike[], callback: ResponseCallback<DataValue[]>): void;
553
-
554
554
  public async readVariableValue(nodeId: NodeIdLike): Promise<DataValue>;
555
-
556
555
  public async readVariableValue(nodeIds: NodeIdLike[]): Promise<DataValue[]>;
557
556
  /**
558
557
  * @internal
@@ -1999,7 +1998,7 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession {
1999
1998
  });
2000
1999
  }
2001
2000
 
2002
- public startKeepAliveManager(): void {
2001
+ public startKeepAliveManager(keepAliveInterval?: number): void {
2003
2002
  if (this._keepAliveManager) {
2004
2003
  // "keepAliveManger already started"
2005
2004
  return;
@@ -2020,7 +2019,7 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession {
2020
2019
  */
2021
2020
  this.emit("keepalive", state, count);
2022
2021
  });
2023
- this._keepAliveManager.start();
2022
+ this._keepAliveManager.start(keepAliveInterval);
2024
2023
  }
2025
2024
 
2026
2025
  public stopKeepAliveManager(): void {
@@ -2074,6 +2073,12 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession {
2074
2073
  str += "\n reviseTokenLifetime...... " + this._client._secureChannel.securityToken.revisedLifetime;
2075
2074
  }
2076
2075
  }
2076
+ str += "\n keepAlive ................ " + this._keepAliveManager ? true: false;
2077
+ if (this._keepAliveManager) {
2078
+ str += "\n keepAlive checkInterval.. " + this._keepAliveManager.checkInterval;
2079
+ str += "\n defaultTransportTimeout.. " + ClientSecureChannelLayer.defaultTransportTimeout;
2080
+
2081
+ }
2077
2082
  return str;
2078
2083
  }
2079
2084
 
@@ -1049,7 +1049,7 @@ export class OPCUAClientImpl extends ClientBaseImpl implements OPCUAClient {
1049
1049
  session.serverNonce = response.serverNonce;
1050
1050
  session.lastResponseReceivedTime = new Date();
1051
1051
  if (this.keepSessionAlive) {
1052
- session.startKeepAliveManager();
1052
+ session.startKeepAliveManager(this.keepAliveInterval);
1053
1053
  }
1054
1054
  session.userIdentityInfo = userIdentityInfo;
1055
1055
  return callback(null, session);
@@ -11,6 +11,7 @@ import { TransferSubscriptionsRequest, TransferSubscriptionsResponse } from "nod
11
11
  import { CallbackT, StatusCode, StatusCodes } from "node-opcua-status-code";
12
12
  import { ErrorCallback } from "node-opcua-status-code";
13
13
  import { CloseSessionRequest } from "node-opcua-types";
14
+ import { invalidateExtraDataTypeManager } from "node-opcua-client-dynamic-extension-object";
14
15
 
15
16
  import { SubscriptionId } from "./client_session";
16
17
  import { ClientSessionImpl, Reconnectable } from "./private/client_session_impl";
@@ -166,6 +167,11 @@ function repair_client_session_by_recreating_a_new_session(
166
167
  session: ClientSessionImpl,
167
168
  callback: (err?: Error) => void
168
169
  ) {
170
+
171
+ // As we don"t know if server has been rebooted or not,
172
+ // and may be upgraded in between, we have to invalidate the extra data type manager
173
+ invalidateExtraDataTypeManager(session);
174
+
169
175
  if (doDebug) {
170
176
  doDebug && debugLog(" repairing client session by_recreating a new session for old session ", session.sessionId.toString());
171
177
  }
@@ -210,13 +216,17 @@ function repair_client_session_by_recreating_a_new_session(
210
216
  newSession,
211
217
  newSession.userIdentityInfo!,
212
218
  (err: Error | null, session1?: ClientSessionImpl) => {
213
- doDebug && debugLog(chalk.bgWhite.cyan(" => activating a new session .... Done err=", err ? err.message : "null"));
214
- if (err) {
215
- doDebug && debugLog(
216
- chalk.bgWhite.cyan(
217
- "reactivation of the new session has failed: let be smart and close it before failing this repair attempt"
218
- )
219
+ doDebug &&
220
+ debugLog(
221
+ chalk.bgWhite.cyan(" => activating a new session .... Done err=", err ? err.message : "null")
219
222
  );
223
+ if (err) {
224
+ doDebug &&
225
+ debugLog(
226
+ chalk.bgWhite.cyan(
227
+ "reactivation of the new session has failed: let be smart and close it before failing this repair attempt"
228
+ )
229
+ );
220
230
  // but just on the server side, not on the client side
221
231
  const closeSessionRequest = new CloseSessionRequest({
222
232
  deleteSubscriptions: true
@@ -277,10 +287,11 @@ function repair_client_session_by_recreating_a_new_session(
277
287
 
278
288
  // istanbul ignore next
279
289
  if (doDebug) {
280
- doDebug && debugLog(
281
- chalk.cyan(" => transfer subscriptions done"),
282
- results.map((x: any) => x.statusCode.toString()).join(" ")
283
- );
290
+ doDebug &&
291
+ debugLog(
292
+ chalk.cyan(" => transfer subscriptions done"),
293
+ results.map((x: any) => x.statusCode.toString()).join(" ")
294
+ );
284
295
  }
285
296
 
286
297
  const subscriptionsToRecreate = [];
@@ -291,22 +302,24 @@ function repair_client_session_by_recreating_a_new_session(
291
302
  const statusCode = results[i].statusCode;
292
303
  if (statusCode.equals(StatusCodes.BadSubscriptionIdInvalid)) {
293
304
  // repair subscription
294
- doDebug && debugLog(
295
- chalk.red(" WARNING SUBSCRIPTION "),
296
- subscriptionsIds[i],
297
- chalk.red(" SHOULD BE RECREATED")
298
- );
305
+ doDebug &&
306
+ debugLog(
307
+ chalk.red(" WARNING SUBSCRIPTION "),
308
+ subscriptionsIds[i],
309
+ chalk.red(" SHOULD BE RECREATED")
310
+ );
299
311
 
300
312
  subscriptionsToRecreate.push(subscriptionsIds[i]);
301
313
  } else {
302
314
  const availableSequenceNumbers = results[i].availableSequenceNumbers;
303
315
 
304
- doDebug && debugLog(
305
- chalk.green(" SUBSCRIPTION "),
306
- subscriptionsIds[i],
307
- chalk.green(" CAN BE REPAIRED AND AVAILABLE "),
308
- availableSequenceNumbers
309
- );
316
+ doDebug &&
317
+ debugLog(
318
+ chalk.green(" SUBSCRIPTION "),
319
+ subscriptionsIds[i],
320
+ chalk.green(" CAN BE REPAIRED AND AVAILABLE "),
321
+ availableSequenceNumbers
322
+ );
310
323
  // should be Good.
311
324
  }
312
325
  }
@@ -329,10 +342,11 @@ function repair_client_session_by_recreating_a_new_session(
329
342
  doDebug && debugLog("_recreateSubscription failed !" + err1.message);
330
343
  }
331
344
 
332
- doDebug && debugLog(
333
- chalk.cyan(" => RECREATING SUBSCRIPTION AND MONITORED ITEM DONE "),
334
- subscriptionId
335
- );
345
+ doDebug &&
346
+ debugLog(
347
+ chalk.cyan(" => RECREATING SUBSCRIPTION AND MONITORED ITEM DONE "),
348
+ subscriptionId
349
+ );
336
350
 
337
351
  next();
338
352
  });
@@ -379,7 +393,8 @@ function repair_client_session_by_recreating_a_new_session(
379
393
 
380
394
  function _repair_client_session(client: IClientBase, session: ClientSessionImpl, callback: (err?: Error) => void): void {
381
395
  const callback2 = (err2?: Error) => {
382
- doDebug && debugLog("Session repair completed with err: ", err2 ? err2.message : "<no error>", session.sessionId.toString());
396
+ doDebug &&
397
+ debugLog("Session repair completed with err: ", err2 ? err2.message : "<no error>", session.sessionId.toString());
383
398
  session.emit("session_repaired");
384
399
  callback(err2);
385
400
  };