node-opcua-client 2.100.0 → 2.102.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.
@@ -2,6 +2,7 @@
2
2
  * @module node-opcua-client-private
3
3
  */
4
4
  import { EventEmitter } from "events";
5
+ import { callbackify } from "util";
5
6
  import * as chalk from "chalk";
6
7
  import { assert } from "node-opcua-assert";
7
8
  import { AggregateFunction } from "node-opcua-constants";
@@ -22,7 +23,7 @@ import { ExtensionObject } from "node-opcua-extension-object";
22
23
  import { coerceNodeId, NodeId, NodeIdLike, resolveNodeId } from "node-opcua-nodeid";
23
24
  import { getBuiltInDataType, getArgumentDefinitionHelper, IBasicSession, IBasicTransportSettings } from "node-opcua-pseudo-session";
24
25
  import { AnyConstructorFunc } from "node-opcua-schemas";
25
- import { requestHandleNotSetValue, SignatureData } from "node-opcua-secure-channel";
26
+ import { ClientSecureChannelLayer, requestHandleNotSetValue, SignatureData } from "node-opcua-secure-channel";
26
27
  import { BrowseDescription, BrowseRequest, BrowseResponse, BrowseResult } from "node-opcua-service-browse";
27
28
  import { CallMethodRequest, CallMethodResult, CallRequest, CallResponse } from "node-opcua-service-call";
28
29
  import { EndpointDescription } from "node-opcua-service-endpoints";
@@ -293,7 +294,6 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession {
293
294
  this.timeout = 0;
294
295
  }
295
296
 
296
-
297
297
  getTransportSettings(): IBasicTransportSettings {
298
298
  return this._client!.getTransportSettings();
299
299
  }
@@ -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
@@ -1556,19 +1555,27 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession {
1556
1555
  return this._client !== null && this._client._secureChannel !== null && this._client._secureChannel.isOpened();
1557
1556
  }
1558
1557
 
1558
+ private requestReconnection() {
1559
+ if (this._client) {
1560
+ this._client.requestReconnection();
1561
+ assert(this._client.isReconnecting === true, "expecting client to be reconnecting now");
1562
+ }
1563
+ }
1564
+
1559
1565
  public performMessageTransaction(request: Request, callback: (err: Error | null, response?: Response) => void): void {
1560
1566
  if (!this._client) {
1561
1567
  // session may have been closed by user ... but is still in used !!
1562
1568
  return callback(new Error("Session has been closed and should not be used to perform a transaction anymore"));
1563
1569
  }
1564
1570
 
1565
- if (!this.isChannelValid()) {
1566
- // the secure channel is broken, may be the server has crashed or the network cable has been disconnected
1567
- // for a long time
1568
- // we may need to queue this transaction, as a secure token may be being reprocessed
1569
- debugLog(chalk.bgWhite.red("!!! Performing transaction on invalid channel !!! ", request.constructor.name));
1570
- return callback(new Error("Invalid Channel after performing transaction on " + request.constructor.name));
1571
- }
1571
+ // if (!this.isChannelValid()) {
1572
+ // // the secure channel is broken, may be the server has crashed or the network cable has been disconnected
1573
+ // // for a long time
1574
+ // // we may need to queue this transaction, as a secure token may be being reprocessed
1575
+ // errorLog(chalk.bgWhite.red("!!! Performing transaction on invalid channel !!! ", request.schema.name));
1576
+ // // this.requestReconnection();
1577
+ // return callback(new Error("!!! Performing transaction on invalid channel with " + request.schema.name + ": starting reconnection process"));
1578
+ // }
1572
1579
 
1573
1580
  this._reconnecting.pendingTransactions = this._reconnecting.pendingTransactions || [];
1574
1581
  this._reconnecting.pendingTransactionsCount = this._reconnecting.pendingTransactionsCount || 0;
@@ -2014,7 +2021,7 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession {
2014
2021
  });
2015
2022
  this._keepAliveManager.start();
2016
2023
  }
2017
-
2024
+
2018
2025
  public stopKeepAliveManager(): void {
2019
2026
  if (this._keepAliveManager) {
2020
2027
  this._keepAliveManager.stop();
@@ -2066,6 +2073,12 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession {
2066
2073
  str += "\n reviseTokenLifetime...... " + this._client._secureChannel.securityToken.revisedLifetime;
2067
2074
  }
2068
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
+ }
2069
2082
  return str;
2070
2083
  }
2071
2084
 
@@ -2300,19 +2313,18 @@ async function promoteOpaqueStructure2(session: IBasicSession, callMethodResult:
2300
2313
  function countOpaqueStructures(callMethodResults: CallMethodResult[]): number {
2301
2314
  const x = (a: Variant[] | null): PseudoDataValue[] => {
2302
2315
  if (a === null) return [] as PseudoDataValue[];
2303
- return a.map((value) => { return { value: value } });
2304
- }
2305
- const opaqueStructureCount = callMethodResults.reduce(
2306
- (prev, callMethodResult) => {
2307
- return prev + extractDataValueToPromote(x(callMethodResult.outputArguments)).length;
2308
- }, 0);
2316
+ return a.map((value) => {
2317
+ return { value: value };
2318
+ });
2319
+ };
2320
+ const opaqueStructureCount = callMethodResults.reduce((prev, callMethodResult) => {
2321
+ return prev + extractDataValueToPromote(x(callMethodResult.outputArguments)).length;
2322
+ }, 0);
2309
2323
  return opaqueStructureCount;
2310
2324
  }
2311
2325
  async function promoteOpaqueStructure3(session: IBasicSession, callMethodResults: CallMethodResult[]): Promise<void> {
2312
-
2313
2326
  const opaqueStructureCount = countOpaqueStructures(callMethodResults);
2314
- if (0 === opaqueStructureCount)
2315
- return;
2327
+ if (0 === opaqueStructureCount) return;
2316
2328
 
2317
2329
  // construct dataTypeManager if not already present
2318
2330
  await getExtraDataTypeManager(session);
@@ -2324,11 +2336,11 @@ async function promoteOpaqueStructure3(session: IBasicSession, callMethodResults
2324
2336
  // tslint:disable:no-var-requires
2325
2337
  // tslint:disable:max-line-length
2326
2338
  const thenify = require("thenify");
2327
- const callbackify = require("callbackify");
2328
2339
  const opts = { multiArgs: false };
2329
2340
 
2330
2341
  const promoteOpaqueStructureWithCallback = callbackify(promoteOpaqueStructure);
2331
2342
  const promoteOpaqueStructure3WithCallback = callbackify(promoteOpaqueStructure3) as promoteOpaqueStructure3WithCallbackFunc;
2343
+ // ClientSessionImpl.prototype.constructExtensionObject = callbackify(ClientSessionImpl.prototype.constructExtensionObject) as any;
2332
2344
 
2333
2345
  ClientSessionImpl.prototype.browse = thenify.withCallback(ClientSessionImpl.prototype.browse, opts);
2334
2346
  ClientSessionImpl.prototype.browseNext = thenify.withCallback(ClientSessionImpl.prototype.browseNext, opts);
@@ -2367,5 +2379,4 @@ ClientSessionImpl.prototype.registerNodes = thenify.withCallback(ClientSessionIm
2367
2379
  ClientSessionImpl.prototype.unregisterNodes = thenify.withCallback(ClientSessionImpl.prototype.unregisterNodes, opts);
2368
2380
  ClientSessionImpl.prototype.readNamespaceArray = thenify.withCallback(ClientSessionImpl.prototype.readNamespaceArray, opts);
2369
2381
  ClientSessionImpl.prototype.getBuiltInDataType = thenify.withCallback(ClientSessionImpl.prototype.getBuiltInDataType, opts);
2370
- ClientSessionImpl.prototype.constructExtensionObject = callbackify(ClientSessionImpl.prototype.constructExtensionObject);
2371
2382
  ClientSessionImpl.prototype.changeUser = thenify.withCallback(ClientSessionImpl.prototype.changeUser, opts);
@@ -26,4 +26,6 @@ export interface IClientBase {
26
26
  _sessions: ClientSessionImpl[];
27
27
 
28
28
  getTransportSettings(): IBasicTransportSettings;
29
+
30
+ requestReconnection(): void;
29
31
  }
@@ -755,10 +755,10 @@ export class OPCUAClientImpl extends ClientBaseImpl implements OPCUAClient {
755
755
  }
756
756
  // istanbul ignore next
757
757
  if (!this.__resolveEndPoint() || !this.endpoint) {
758
- return callback!(
758
+ return callback!(
759
759
  new Error(
760
- " End point must exist " + this._secureChannel!.endpointUrl +
761
- " securityMode = " + MessageSecurityMode[this.securityMode] +
760
+ " End point must exist " + this._secureChannel!.endpointUrl +
761
+ " securityMode = " + MessageSecurityMode[this.securityMode] +
762
762
  " securityPolicy = " + this.securityPolicy));
763
763
  }
764
764
 
@@ -805,10 +805,7 @@ export class OPCUAClientImpl extends ClientBaseImpl implements OPCUAClient {
805
805
  * @private
806
806
  */
807
807
  public _on_connection_reestablished(callback: (err?: Error) => void): void {
808
- assert(typeof callback === "function");
809
-
810
- // call base class implementation first
811
- ClientBaseImpl.prototype._on_connection_reestablished.call(this, (/*err?: Error*/) => {
808
+ super._on_connection_reestablished((/*err?: Error*/) => {
812
809
  repair_client_sessions(this, callback);
813
810
  });
814
811
  }
@@ -849,7 +846,7 @@ export class OPCUAClientImpl extends ClientBaseImpl implements OPCUAClient {
849
846
  this.clientNonce = crypto.randomBytes(32);
850
847
 
851
848
  // recycle session name if already exists
852
- const sessionName = session.name
849
+ const sessionName = session.name
853
850
 
854
851
  const request = new CreateSessionRequest({
855
852
  clientCertificate: this.getCertificate(),
@@ -1163,15 +1160,15 @@ export class OPCUAClientImpl extends ClientBaseImpl implements OPCUAClient {
1163
1160
  this._serverEndpoints.map(
1164
1161
  (endpoint) =>
1165
1162
  endpoint.endpointUrl + " "
1166
- + MessageSecurityMode[endpoint.securityMode] + " "
1167
- + endpoint.securityPolicyUri + " "
1168
- + endpoint.userIdentityTokens?.map((u)=> UserTokenType[u.tokenType]).join(",")
1163
+ + MessageSecurityMode[endpoint.securityMode] + " "
1164
+ + endpoint.securityPolicyUri + " "
1165
+ + endpoint.userIdentityTokens?.map((u) => UserTokenType[u.tokenType]).join(",")
1169
1166
  ).join('\n')
1170
1167
  );
1171
1168
  }
1172
1169
  return callback(new Error(
1173
- " End point must exist " + this._secureChannel!.endpointUrl +
1174
- " securityMode = " + MessageSecurityMode[this.securityMode] +
1170
+ " End point must exist " + this._secureChannel!.endpointUrl +
1171
+ " securityMode = " + MessageSecurityMode[this.securityMode] +
1175
1172
  " securityPolicy = " + this.securityPolicy));
1176
1173
  }
1177
1174
  this.serverUri = this.endpoint.server.applicationUri || "invalid application uri";
@@ -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";
@@ -139,7 +140,7 @@ function create_session_and_repeat_if_failed(
139
140
  callback: CallbackT<ClientSessionImpl>
140
141
  ) {
141
142
  if (session.hasBeenClosed()) {
142
- doDebug && debugLog("Cannot complete subscription republish due to session terminatio");
143
+ doDebug && debugLog("Cannot complete subscription republish due to session termination");
143
144
  return callback(new Error("Cannot complete subscription republish due to session termination"));
144
145
  }
145
146
  doDebug && debugLog(chalk.bgWhite.red(" => creating a new session ...."));
@@ -147,7 +148,7 @@ function create_session_and_repeat_if_failed(
147
148
  // so we can reuse subscriptions data
148
149
  client.__createSession_step2(session, (err: Error | null, session1?: ClientSessionImpl) => {
149
150
  if (err && session.hasBeenClosed()) {
150
- doDebug && debugLog("Cannot complete subscription republish due to session terminatio");
151
+ doDebug && debugLog("Cannot complete subscription republish due to session termination");
151
152
  return callback(new Error("Cannot complete subscription republish due to session termination"));
152
153
  }
153
154
  if (!err && session1) {
@@ -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
  };
@@ -433,9 +448,14 @@ export function repair_client_session(client: IClientBase, session: ClientSessio
433
448
  session.sessionId.toString(),
434
449
  " => Let's retry"
435
450
  );
436
- setTimeout(() => {
437
- _repair_client_session(client, session, callback);
438
- }, 2000);
451
+ if (!session.hasBeenClosed()) {
452
+ setTimeout(() => {
453
+ _repair_client_session(client, session, callback);
454
+ }, 2000);
455
+ } else {
456
+ // session does not need to be repaired anymore
457
+ callback();
458
+ }
439
459
  return;
440
460
  }
441
461
  doDebug && debugLog(chalk.yellow("session has been restored"), session.sessionId.toString());
@@ -452,9 +472,8 @@ export function repair_client_session(client: IClientBase, session: ClientSessio
452
472
  }
453
473
 
454
474
  export function repair_client_sessions(client: IClientBase, callback: (err?: Error) => void): void {
455
- const self = client;
456
475
  // repair session
457
- const sessions = self.getSessions();
476
+ const sessions = client.getSessions();
458
477
  doDebug && debugLog(chalk.red.bgWhite(" Starting sessions reactivation", sessions.length));
459
478
  async.map(
460
479
  sessions,