node-opcua-server 2.156.0 → 2.158.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 (54) hide show
  1. package/dist/addressSpace_accessor.js +1 -0
  2. package/dist/addressSpace_accessor.js.map +1 -1
  3. package/dist/base_server.js +7 -1
  4. package/dist/base_server.js.map +1 -1
  5. package/dist/factory.js +1 -0
  6. package/dist/factory.js.map +1 -1
  7. package/dist/history_server_capabilities.js +14 -0
  8. package/dist/history_server_capabilities.js.map +1 -1
  9. package/dist/i_register_server_manager.d.ts +63 -3
  10. package/dist/i_register_server_manager.js +47 -0
  11. package/dist/i_register_server_manager.js.map +1 -1
  12. package/dist/i_server_side_publish_engine.js +4 -0
  13. package/dist/i_server_side_publish_engine.js.map +1 -1
  14. package/dist/index.d.ts +22 -0
  15. package/dist/index.js +22 -0
  16. package/dist/index.js.map +1 -1
  17. package/dist/monitored_item.d.ts +3 -6
  18. package/dist/monitored_item.js +44 -17
  19. package/dist/monitored_item.js.map +1 -1
  20. package/dist/opcua_server.js +44 -26
  21. package/dist/opcua_server.js.map +1 -1
  22. package/dist/queue.js +2 -1
  23. package/dist/queue.js.map +1 -1
  24. package/dist/register_server_manager.d.ts +14 -26
  25. package/dist/register_server_manager.js +282 -293
  26. package/dist/register_server_manager.js.map +1 -1
  27. package/dist/register_server_manager_hidden.d.ts +4 -3
  28. package/dist/register_server_manager_hidden.js +7 -5
  29. package/dist/register_server_manager_hidden.js.map +1 -1
  30. package/dist/register_server_manager_mdns_only.d.ts +5 -3
  31. package/dist/register_server_manager_mdns_only.js +25 -14
  32. package/dist/register_server_manager_mdns_only.js.map +1 -1
  33. package/dist/server_capabilities.js +33 -0
  34. package/dist/server_capabilities.js.map +1 -1
  35. package/dist/server_end_point.js +29 -3
  36. package/dist/server_end_point.js.map +1 -1
  37. package/dist/server_engine.js +20 -3
  38. package/dist/server_engine.js.map +1 -1
  39. package/dist/server_publish_engine.js +95 -98
  40. package/dist/server_publish_engine.js.map +1 -1
  41. package/dist/server_session.js +33 -5
  42. package/dist/server_session.js.map +1 -1
  43. package/dist/server_subscription.js +70 -15
  44. package/dist/server_subscription.js.map +1 -1
  45. package/dist/user_manager.js +1 -0
  46. package/dist/user_manager.js.map +1 -1
  47. package/package.json +49 -47
  48. package/source/i_register_server_manager.ts +66 -5
  49. package/source/index.ts +22 -0
  50. package/source/monitored_item.ts +28 -19
  51. package/source/opcua_server.ts +33 -23
  52. package/source/register_server_manager.ts +294 -354
  53. package/source/register_server_manager_hidden.ts +6 -5
  54. package/source/register_server_manager_mdns_only.ts +25 -14
@@ -3,13 +3,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.RegisterServerManager = exports.RegisterServerManagerStatus = void 0;
6
+ exports.RegisterServerManager = void 0;
7
7
  /**
8
8
  * @module node-opcua-server
9
9
  */
10
10
  // tslint:disable:no-console
11
11
  const events_1 = require("events");
12
- const async_1 = __importDefault(require("async"));
13
12
  const chalk_1 = __importDefault(require("chalk"));
14
13
  const node_opcua_assert_1 = require("node-opcua-assert");
15
14
  const node_opcua_client_1 = require("node-opcua-client");
@@ -19,17 +18,10 @@ const node_opcua_secure_channel_1 = require("node-opcua-secure-channel");
19
18
  const node_opcua_service_discovery_1 = require("node-opcua-service-discovery");
20
19
  const node_opcua_types_1 = require("node-opcua-types");
21
20
  const web_1 = require("node-opcua-crypto/web");
22
- const doDebug = (0, node_opcua_debug_1.checkDebugFlag)(__filename);
23
- const debugLog = (0, node_opcua_debug_1.make_debugLog)(__filename);
24
- const warningLog = (0, node_opcua_debug_1.make_warningLog)(__filename);
25
- var RegisterServerManagerStatus;
26
- (function (RegisterServerManagerStatus) {
27
- RegisterServerManagerStatus[RegisterServerManagerStatus["INACTIVE"] = 1] = "INACTIVE";
28
- RegisterServerManagerStatus[RegisterServerManagerStatus["INITIALIZING"] = 2] = "INITIALIZING";
29
- RegisterServerManagerStatus[RegisterServerManagerStatus["REGISTERING"] = 3] = "REGISTERING";
30
- RegisterServerManagerStatus[RegisterServerManagerStatus["WAITING"] = 4] = "WAITING";
31
- RegisterServerManagerStatus[RegisterServerManagerStatus["UNREGISTERING"] = 5] = "UNREGISTERING";
32
- })(RegisterServerManagerStatus || (exports.RegisterServerManagerStatus = RegisterServerManagerStatus = {}));
21
+ const i_register_server_manager_1 = require("./i_register_server_manager");
22
+ const doDebug = (0, node_opcua_debug_1.checkDebugFlag)("REGISTER_LDS");
23
+ const debugLog = (0, node_opcua_debug_1.make_debugLog)("REGISTER_LDS");
24
+ const warningLog = (0, node_opcua_debug_1.make_warningLog)("REGISTER_LDS");
33
25
  const g_DefaultRegistrationServerTimeout = 8 * 60 * 1000; // 8 minutes
34
26
  function securityPolicyLevel(securityPolicy) {
35
27
  switch (securityPolicy) {
@@ -100,7 +92,7 @@ function findSecureEndpoint(endpoints) {
100
92
  }
101
93
  function constructRegisteredServer(server, isOnline) {
102
94
  const discoveryUrls = server.getDiscoveryUrls();
103
- (0, node_opcua_assert_1.assert)(!isOnline || discoveryUrls.length >= 1, "expecting some discoveryUrls if we go online ....");
95
+ (0, node_opcua_assert_1.assert)(!isOnline || discoveryUrls.length >= 1, "expecting some discoveryUrls if we go online .... ");
104
96
  const info = (0, web_1.exploreCertificate)(server.getCertificate());
105
97
  const commonName = info.tbsCertificate.subject.commonName;
106
98
  const serverUri = info.tbsCertificate.extensions?.subjectAltName.uniformResourceIdentifier[0];
@@ -150,9 +142,9 @@ function constructRegisterServer2Request(serverB, isOnline) {
150
142
  server
151
143
  });
152
144
  }
153
- const no_reconnect_connectivity_strategy = {
154
- initialDelay: 2000,
155
- maxDelay: 50000,
145
+ const no_retry_connectivity_strategy = {
146
+ initialDelay: 1000,
147
+ maxDelay: 2000,
156
148
  maxRetry: 1, // NO RETRY !!!
157
149
  randomisationFactor: 0
158
150
  };
@@ -162,17 +154,17 @@ const infinite_connectivity_strategy = {
162
154
  maxRetry: 10000000,
163
155
  randomisationFactor: 0
164
156
  };
165
- function sendRegisterServerRequest(server, client, isOnline, callback) {
157
+ const pause = async (duration) => await new Promise((resolve) => setTimeout(resolve, duration));
158
+ async function sendRegisterServerRequest(server, client, isOnline) {
166
159
  // try to send a RegisterServer2Request
167
160
  const request = constructRegisterServer2Request(server, isOnline);
168
- client.performMessageTransaction(request, (err, response) => {
169
- if (!err) {
170
- // RegisterServerResponse
171
- debugLog("RegisterServerManager#_registerServer sendRegisterServer2Request has succeeded (isOnline", isOnline, ")");
172
- (0, node_opcua_assert_1.assert)(response instanceof node_opcua_service_discovery_1.RegisterServer2Response);
173
- callback(err);
174
- }
175
- else {
161
+ await new Promise((resolve, reject) => {
162
+ client.performMessageTransaction(request, (err, response) => {
163
+ if (!err) {
164
+ // RegisterServerResponse
165
+ debugLog("RegisterServerManager#_registerServer sendRegisterServer2Request has succeeded (isOnline", isOnline, ")");
166
+ return resolve();
167
+ }
176
168
  debugLog("RegisterServerManager#_registerServer sendRegisterServer2Request has failed " + "(isOnline", isOnline, ")");
177
169
  debugLog("RegisterServerManager#_registerServer" + " falling back to using sendRegisterServerRequest instead");
178
170
  // fall back to
@@ -180,26 +172,24 @@ function sendRegisterServerRequest(server, client, isOnline, callback) {
180
172
  client.performMessageTransaction(request1, (err1, response1) => {
181
173
  if (!err1) {
182
174
  debugLog("RegisterServerManager#_registerServer sendRegisterServerRequest " + "has succeeded (isOnline", isOnline, ")");
183
- (0, node_opcua_assert_1.assert)(response1 instanceof node_opcua_service_discovery_1.RegisterServerResponse);
184
- }
185
- else {
186
- debugLog("RegisterServerManager#_registerServer sendRegisterServerRequest " + "has failed (isOnline", isOnline, ")");
175
+ return resolve();
187
176
  }
188
- callback(err1);
177
+ debugLog("RegisterServerManager#_registerServer sendRegisterServerRequest " + "has failed (isOnline", isOnline, ")");
178
+ reject(err1);
189
179
  });
190
- }
180
+ });
191
181
  });
192
182
  }
193
183
  let g_registeringClientCounter = 0;
194
184
  /**
195
185
  * RegisterServerManager is responsible to Register an opcua server on a LDS or LDS-ME server
196
186
  * This class takes in charge :
197
- * - the initial registration of a server
198
- * - the regular registration renewal (every 8 minutes or so ...)
199
- * - dealing with cases where LDS is not up and running when server starts.
200
- * ( in this case the connection will be continuously attempted using the infinite
201
- * back-off strategy
202
- * - the un-registration of the server ( during shutdown for instance)
187
+ * - the initial registration of a server
188
+ * - the regular registration renewal (every 8 minutes or so ...)
189
+ * - dealing with cases where LDS is not up and running when server starts.
190
+ * ( in this case the connection will be continuously attempted using the infinite
191
+ * back-off strategy
192
+ * - the un-registration of the server ( during shutdown for instance)
203
193
  *
204
194
  * Events:
205
195
  *
@@ -223,13 +213,21 @@ let g_registeringClientCounter = 0;
223
213
  * (LDS => Local Discovery Server)
224
214
  */
225
215
  class RegisterServerManager extends events_1.EventEmitter {
216
+ discoveryServerEndpointUrl;
217
+ timeout;
218
+ server;
219
+ _registrationTimerId;
220
+ state = i_register_server_manager_1.RegisterServerManagerStatus.INACTIVE;
221
+ _registration_client = null;
222
+ selectedEndpoint;
223
+ _serverEndpoints = [];
224
+ getState() {
225
+ return this.state;
226
+ }
226
227
  constructor(options) {
227
228
  super();
228
- this.state = RegisterServerManagerStatus.INACTIVE;
229
- this._registration_client = null;
230
- this._serverEndpoints = [];
231
229
  this.server = options.server;
232
- this._setState(RegisterServerManagerStatus.INACTIVE);
230
+ this.#_setState(i_register_server_manager_1.RegisterServerManagerStatus.INACTIVE);
233
231
  this.timeout = g_DefaultRegistrationServerTimeout;
234
232
  this.discoveryServerEndpointUrl = options.discoveryServerEndpointUrl || "opc.tcp://localhost:4840";
235
233
  (0, node_opcua_assert_1.assert)(typeof this.discoveryServerEndpointUrl === "string");
@@ -238,7 +236,6 @@ class RegisterServerManager extends events_1.EventEmitter {
238
236
  dispose() {
239
237
  this.server = null;
240
238
  debugLog("RegisterServerManager#dispose", this.state.toString());
241
- (0, node_opcua_assert_1.assert)(this.state === RegisterServerManagerStatus.INACTIVE);
242
239
  if (this._registrationTimerId) {
243
240
  clearTimeout(this._registrationTimerId);
244
241
  this._registrationTimerId = null;
@@ -246,335 +243,327 @@ class RegisterServerManager extends events_1.EventEmitter {
246
243
  (0, node_opcua_assert_1.assert)(this._registrationTimerId === null, "stop has not been called");
247
244
  this.removeAllListeners();
248
245
  }
249
- _emitEvent(eventName) {
246
+ #_emitEvent(eventName) {
250
247
  setImmediate(() => {
248
+ debugLog("emiting event", eventName);
251
249
  this.emit(eventName);
252
250
  });
253
251
  }
254
- _setState(status) {
255
- const previousState = this.state || RegisterServerManagerStatus.INACTIVE;
256
- debugLog("RegisterServerManager#setState : ", RegisterServerManagerStatus[previousState], " => ", RegisterServerManagerStatus[status]);
252
+ #_setState(status) {
253
+ const previousState = this.state || i_register_server_manager_1.RegisterServerManagerStatus.INACTIVE;
254
+ debugLog("RegisterServerManager#setState : ", i_register_server_manager_1.RegisterServerManagerStatus[previousState], " => ", i_register_server_manager_1.RegisterServerManagerStatus[status]);
257
255
  this.state = status;
258
256
  }
259
- start(callback) {
257
+ /**
258
+ * The start method initiates the registration process in a non-blocking way.
259
+ * It immediately returns while the actual work is performed in a background task.
260
+ */
261
+ async start() {
260
262
  debugLog("RegisterServerManager#start");
261
- if (this.state !== RegisterServerManagerStatus.INACTIVE) {
262
- return callback(new Error("RegisterServer process already started")); // already started
263
+ if (this.state !== i_register_server_manager_1.RegisterServerManagerStatus.INACTIVE) {
264
+ throw new Error("RegisterServer process already started: " + i_register_server_manager_1.RegisterServerManagerStatus[this.state]);
263
265
  }
264
266
  this.discoveryServerEndpointUrl = (0, node_opcua_hostname_1.resolveFullyQualifiedDomainName)(this.discoveryServerEndpointUrl);
265
- // perform initial registration + automatic renewal
266
- this._establish_initial_connection((err) => {
267
- if (err) {
268
- debugLog("RegisterServerManager#start => _establish_initial_connection has failed");
269
- return callback(err);
270
- }
271
- if (this.state !== RegisterServerManagerStatus.INITIALIZING) {
272
- debugLog("RegisterServerManager#start => _establish_initial_connection has failed");
273
- return callback();
274
- }
275
- this._registerServer(true, (err1) => {
276
- if (this.state !== RegisterServerManagerStatus.REGISTERING) {
277
- debugLog("RegisterServerManager#start )=> Registration has been cancelled");
278
- return callback(new Error("Registration has been cancelled"));
267
+ // Immediately set the state to INITIALIZING and run the process in the background.
268
+ this.#_setState(i_register_server_manager_1.RegisterServerManagerStatus.INITIALIZING);
269
+ // This method is called without await to ensure it is non-blocking.
270
+ // The catch block handles any synchronous errors.
271
+ this.#_runRegistrationProcess().catch((err) => {
272
+ warningLog("Synchronous error in #_runRegistrationProcess: ", (err?.message));
273
+ });
274
+ }
275
+ /**
276
+ * Private method to run the entire registration process in the background.
277
+ * It handles the state machine transitions and re-connection logic.
278
+ * @private
279
+ */
280
+ async #_runRegistrationProcess() {
281
+ while (this.getState() !== i_register_server_manager_1.RegisterServerManagerStatus.WAITING && !this.#_isTerminating()) {
282
+ debugLog("RegisterServerManager#_runRegistrationProcess - state =", i_register_server_manager_1.RegisterServerManagerStatus[this.state], "isTerminating =", this.#_isTerminating());
283
+ try {
284
+ if (this.getState() === i_register_server_manager_1.RegisterServerManagerStatus.INACTIVE) {
285
+ this.#_setState(i_register_server_manager_1.RegisterServerManagerStatus.INITIALIZING);
286
+ }
287
+ await this.#_establish_initial_connection();
288
+ if (this.getState() !== i_register_server_manager_1.RegisterServerManagerStatus.INITIALIZING) {
289
+ debugLog("RegisterServerManager#_runRegistrationProcess: aborted during initialization");
290
+ return;
279
291
  }
280
- if (err1) {
281
- warningLog("RegisterServerManager#start - registering server has failed ! \n" +
282
- "please check that your server certificate is accepted by the LDS");
283
- this._setState(RegisterServerManagerStatus.INACTIVE);
284
- this._emitEvent("serverRegistrationFailure");
292
+ this.#_setState(i_register_server_manager_1.RegisterServerManagerStatus.INITIALIZED);
293
+ this.#_setState(i_register_server_manager_1.RegisterServerManagerStatus.REGISTERING);
294
+ this.#_emitEvent("serverRegistrationPending");
295
+ await this.#_registerOrUnregisterServer(true);
296
+ if (this.getState() !== i_register_server_manager_1.RegisterServerManagerStatus.REGISTERING) {
297
+ debugLog("RegisterServerManager#_runRegistrationProcess: aborted during registration");
298
+ return;
285
299
  }
286
- else {
287
- this._emitEvent("serverRegistered");
288
- this._setState(RegisterServerManagerStatus.WAITING);
289
- this._trigger_next();
300
+ this.#_setState(i_register_server_manager_1.RegisterServerManagerStatus.REGISTERED);
301
+ this.#_emitEvent("serverRegistered");
302
+ this.#_setState(i_register_server_manager_1.RegisterServerManagerStatus.WAITING);
303
+ this.#_trigger_next();
304
+ return;
305
+ }
306
+ catch (err) {
307
+ debugLog("RegisterServerManager#_runRegistrationProcess - operation failed!", (err.message));
308
+ if (!this.#_isTerminating()) {
309
+ this.#_setState(i_register_server_manager_1.RegisterServerManagerStatus.INACTIVE);
310
+ this.#_emitEvent("serverRegistrationFailure");
311
+ await pause(Math.min(5000, this.timeout));
290
312
  }
291
- callback();
292
- });
293
- });
313
+ }
314
+ }
294
315
  }
295
- _establish_initial_connection(outer_callback) {
296
- /* istanbul ignore next */
316
+ #_isTerminating() {
317
+ return this.getState() === i_register_server_manager_1.RegisterServerManagerStatus.UNREGISTERING || this.getState() === i_register_server_manager_1.RegisterServerManagerStatus.DISPOSING;
318
+ }
319
+ /**
320
+ * Establish the initial connection with the Discovery Server to extract best endpoint to use.
321
+ * @private
322
+ */
323
+ async #_establish_initial_connection() {
297
324
  if (!this.server) {
298
- throw new Error("Internal Error");
325
+ this.#_setState(i_register_server_manager_1.RegisterServerManagerStatus.DISPOSING);
326
+ return;
327
+ }
328
+ if (this.state !== i_register_server_manager_1.RegisterServerManagerStatus.INITIALIZING) {
329
+ debugLog("RegisterServerManager#_establish_initial_connection: aborting due to state change");
330
+ return;
299
331
  }
300
332
  debugLog("RegisterServerManager#_establish_initial_connection");
301
333
  (0, node_opcua_assert_1.assert)(!this._registration_client);
302
334
  (0, node_opcua_assert_1.assert)(typeof this.discoveryServerEndpointUrl === "string");
303
- (0, node_opcua_assert_1.assert)(this.state === RegisterServerManagerStatus.INACTIVE);
304
- this._setState(RegisterServerManagerStatus.INITIALIZING);
335
+ (0, node_opcua_assert_1.assert)(this.state === i_register_server_manager_1.RegisterServerManagerStatus.INITIALIZING);
305
336
  this.selectedEndpoint = undefined;
306
337
  const applicationName = (0, node_opcua_client_1.coerceLocalizedText)(this.server.serverInfo.applicationName)?.text || undefined;
307
- // Retry Strategy must be set
308
338
  this.server.serverCertificateManager.referenceCounter++;
309
- const prefix = "Client-" + g_registeringClientCounter++ + " - ";
339
+ const server = this.server;
340
+ const prefix = `Client-${g_registeringClientCounter++}`;
341
+ const action = "initializing";
342
+ const ldsInfo = this.discoveryServerEndpointUrl;
343
+ const serverInfo = this.server?.serverInfo.applicationUri;
344
+ const clientName = `${prefix} for server ${serverInfo} to LDS ${ldsInfo} for ${action}`;
310
345
  const registrationClient = node_opcua_client_1.OPCUAClientBase.create({
311
- clientName: prefix + " Registering Client for Server " + this.server.serverInfo.applicationUri,
346
+ clientName,
312
347
  applicationName,
313
- applicationUri: this.server.serverInfo.applicationUri,
348
+ applicationUri: server.serverInfo.applicationUri,
314
349
  connectionStrategy: infinite_connectivity_strategy,
315
- clientCertificateManager: this.server.serverCertificateManager,
316
- certificateFile: this.server.certificateFile,
317
- privateKeyFile: this.server.privateKeyFile
350
+ clientCertificateManager: server.serverCertificateManager,
351
+ certificateFile: server.certificateFile,
352
+ privateKeyFile: server.privateKeyFile
318
353
  });
319
- this._registration_client = registrationClient;
320
354
  registrationClient.on("backoff", (nbRetry, delay) => {
355
+ if (this.state !== i_register_server_manager_1.RegisterServerManagerStatus.INITIALIZING)
356
+ return; // Ignore event if state has changed
321
357
  debugLog("RegisterServerManager - received backoff");
322
- warningLog(registrationClient.clientName, chalk_1.default.bgWhite.cyan("contacting discovery server backoff "), this.discoveryServerEndpointUrl, " attempt #", nbRetry, " retrying in ", delay / 1000.0, " seconds");
323
- this._emitEvent("serverRegistrationPending");
358
+ debugLog(registrationClient.clientName, chalk_1.default.bgWhite.cyan("contacting discovery server backoff "), this.discoveryServerEndpointUrl, " attempt #", nbRetry, " retrying in ", delay / 1000.0, " seconds");
359
+ this.#_emitEvent("serverRegistrationPending");
324
360
  });
325
- async_1.default.series([
326
- // do_initial_connection_with_discovery_server
327
- (callback) => {
328
- registrationClient.connect(this.discoveryServerEndpointUrl, (err) => {
329
- if (err) {
330
- debugLog("RegisterServerManager#_establish_initial_connection " + ": initial connection to server has failed");
331
- // xx debugLog(err);
332
- }
333
- return callback(err);
334
- });
335
- },
336
- // getEndpoints_on_discovery_server
337
- (callback) => {
338
- registrationClient.getEndpoints((err, endpoints) => {
339
- if (!err) {
340
- const endpoint = findSecureEndpoint(endpoints);
341
- if (!endpoint) {
342
- throw new Error("Cannot find Secure endpoint");
343
- }
344
- if (endpoint.serverCertificate) {
345
- (0, node_opcua_assert_1.assert)(endpoint.serverCertificate);
346
- this.selectedEndpoint = endpoint;
347
- }
348
- else {
349
- this.selectedEndpoint = undefined;
350
- }
351
- }
352
- else {
353
- debugLog("RegisterServerManager#_establish_initial_connection " + ": getEndpointsRequest has failed");
354
- debugLog(err);
355
- }
356
- callback(err);
357
- });
358
- },
359
- // function closing_discovery_server_connection
360
- (callback) => {
361
- this._serverEndpoints = registrationClient._serverEndpoints;
362
- registrationClient.disconnect((err) => {
363
- this._registration_client = null;
364
- callback(err);
365
- });
366
- },
367
- // function wait_a_little_bit
368
- (callback) => {
369
- setTimeout(callback, 10);
370
- }
371
- ], (err) => {
372
- debugLog("-------------------------------", !!err);
373
- if (this.state !== RegisterServerManagerStatus.INITIALIZING) {
374
- debugLog("RegisterServerManager#_establish_initial_connection has been interrupted ", RegisterServerManagerStatus[this.state]);
375
- this._setState(RegisterServerManagerStatus.INACTIVE);
376
- if (this._registration_client) {
377
- this._registration_client.disconnect((err2) => {
378
- this._registration_client = null;
379
- outer_callback(new Error("Initialization has been canceled"));
380
- });
381
- }
382
- else {
383
- outer_callback(new Error("Initialization has been canceled"));
384
- }
361
+ // Keep track of the client to allow cancellation during connect()
362
+ this._registration_client = registrationClient;
363
+ try {
364
+ await registrationClient.connect(this.discoveryServerEndpointUrl);
365
+ if (!this._registration_client)
366
+ return;
367
+ // Re-check state after the long-running connect operation
368
+ if (this.state !== i_register_server_manager_1.RegisterServerManagerStatus.INITIALIZING) {
369
+ debugLog("RegisterServerManager#_establish_initial_connection: aborted after connection");
385
370
  return;
386
371
  }
387
- if (err) {
388
- this._setState(RegisterServerManagerStatus.INACTIVE);
389
- if (this._registration_client) {
390
- this._registration_client.disconnect((err1) => {
391
- this._registration_client = null;
392
- debugLog("#######", !!err1);
393
- outer_callback(err);
394
- });
395
- return;
372
+ const endpoints = await registrationClient.getEndpoints();
373
+ const endpoint = findSecureEndpoint(endpoints);
374
+ if (!endpoint) {
375
+ throw new Error("Cannot find Secure endpoint");
376
+ }
377
+ if (endpoint.serverCertificate) {
378
+ (0, node_opcua_assert_1.assert)(endpoint.serverCertificate);
379
+ this.selectedEndpoint = endpoint;
380
+ }
381
+ else {
382
+ this.selectedEndpoint = undefined;
383
+ }
384
+ this._serverEndpoints = registrationClient._serverEndpoints;
385
+ }
386
+ catch (err) {
387
+ // Do not set state to INACTIVE or rethrow here, let the caller handle it.
388
+ throw err;
389
+ }
390
+ finally {
391
+ if (this._registration_client) {
392
+ const tmp = this._registration_client;
393
+ this._registration_client = null;
394
+ try {
395
+ await tmp.disconnect();
396
+ }
397
+ catch (err) {
398
+ warningLog("RegisterServerManager#_establish_initial_connection: error disconnecting client", err);
396
399
  }
397
400
  }
398
- outer_callback();
399
- });
401
+ }
400
402
  }
401
- _trigger_next() {
403
+ #_trigger_next() {
402
404
  (0, node_opcua_assert_1.assert)(!this._registrationTimerId);
403
- (0, node_opcua_assert_1.assert)(this.state === RegisterServerManagerStatus.WAITING);
404
- // from spec 1.04 part 4:
405
- // The registration process is designed to be platform independent, robust and able to minimize
406
- // problems created by configuration errors. For that reason, Servers shall register themselves more
407
- // than once.
408
- // Under normal conditions, manually launched Servers shall periodically register with the Discovery
409
- // Server as long as they are able to receive connections from Clients. If a Server goes offline then it
410
- // shall register itself once more and indicate that it is going offline. The registration frequency
411
- // should be configurable; however, the maximum is 10 minutes. If an error occurs during registration
412
- // (e.g. the Discovery Server is not running) then the Server shall periodically re-attempt registration.
413
- // The frequency of these attempts should start at 1 second but gradually increase until the
414
- // registration frequency is the same as what it would be if no errors occurred. The recommended
415
- // approach would be to double the period of each attempt until reaching the maximum.
416
- // When an automatically launched Server (or its install program) registers with the Discovery Server
417
- // it shall provide a path to a semaphore file which the Discovery Server can use to determine if the
418
- // Server has been uninstalled from the machine. The Discovery Server shall have read access to
419
- // the file system that contains the file
420
- // install a registration
405
+ (0, node_opcua_assert_1.assert)(this.state === i_register_server_manager_1.RegisterServerManagerStatus.WAITING);
421
406
  debugLog("RegisterServerManager#_trigger_next " + ": installing timeout to perform registerServer renewal (timeout =", this.timeout, ")");
407
+ if (this._registrationTimerId)
408
+ clearTimeout(this._registrationTimerId);
422
409
  this._registrationTimerId = setTimeout(() => {
423
410
  if (!this._registrationTimerId) {
424
411
  debugLog("RegisterServerManager => cancelling re registration");
425
412
  return;
426
413
  }
427
414
  this._registrationTimerId = null;
415
+ if (this.#_isTerminating()) {
416
+ debugLog("RegisterServerManager#_trigger_next : cancelling re registration");
417
+ return;
418
+ }
428
419
  debugLog("RegisterServerManager#_trigger_next : renewing RegisterServer");
429
- this._registerServer(true, (err) => {
430
- if (this.state !== RegisterServerManagerStatus.INACTIVE &&
431
- this.state !== RegisterServerManagerStatus.UNREGISTERING) {
432
- debugLog("RegisterServerManager#_trigger_next : renewed !", err);
433
- this._setState(RegisterServerManagerStatus.WAITING);
434
- this._emitEvent("serverRegistrationRenewed");
435
- this._trigger_next();
420
+ const after_register = (err) => {
421
+ if (!this.#_isTerminating()) {
422
+ debugLog("RegisterServerManager#_trigger_next : renewed ! err:", err?.message);
423
+ this.#_setState(i_register_server_manager_1.RegisterServerManagerStatus.WAITING);
424
+ this.#_emitEvent("serverRegistrationRenewed");
425
+ this.#_trigger_next();
436
426
  }
437
- });
427
+ };
428
+ // State transition before the call
429
+ this.#_setState(i_register_server_manager_1.RegisterServerManagerStatus.REGISTERING);
430
+ this.#_emitEvent("serverRegistrationPending");
431
+ this.#_registerOrUnregisterServer(/*isOnline=*/ true)
432
+ .then(() => after_register())
433
+ .catch((err) => after_register(err));
438
434
  }, this.timeout);
439
435
  }
440
- stop(callback) {
436
+ async stop() {
441
437
  debugLog("RegisterServerManager#stop");
438
+ if (this.#_isTerminating()) {
439
+ debugLog("Already stopping or stopped...");
440
+ return;
441
+ }
442
+ // make sure we don't have any timer running
443
+ // so a registration renewal won't happen while we are stopping
442
444
  if (this._registrationTimerId) {
443
- debugLog("RegisterServerManager#stop :clearing timeout");
444
445
  clearTimeout(this._registrationTimerId);
445
446
  this._registrationTimerId = null;
446
447
  }
447
- this._cancel_pending_client_if_any(() => {
448
- debugLog("RegisterServerManager#stop _cancel_pending_client_if_any done ", this.state);
449
- if (!this.selectedEndpoint || this.state === RegisterServerManagerStatus.INACTIVE) {
450
- this.state = RegisterServerManagerStatus.INACTIVE;
451
- (0, node_opcua_assert_1.assert)(this._registrationTimerId === null);
452
- return callback();
448
+ // Immediately set state to signal a stop
449
+ this.#_setState(i_register_server_manager_1.RegisterServerManagerStatus.UNREGISTERING);
450
+ // Cancel any pending client connections
451
+ await this.#_cancel_pending_client_if_any();
452
+ if (this.selectedEndpoint) {
453
+ try {
454
+ await this.#_registerOrUnregisterServer(/* isOnline= */ false);
455
+ this.#_setState(i_register_server_manager_1.RegisterServerManagerStatus.UNREGISTERED);
456
+ this.#_emitEvent("serverUnregistered");
453
457
  }
454
- this._registerServer(false, () => {
455
- this._setState(RegisterServerManagerStatus.INACTIVE);
456
- this._emitEvent("serverUnregistered");
457
- callback();
458
- });
459
- });
458
+ catch (err) {
459
+ warningLog(err);
460
+ warningLog("RegisterServerManager#stop: Unregistration failed.", err.message);
461
+ }
462
+ }
463
+ // Final state transition to INACTIVE
464
+ this.#_setState(i_register_server_manager_1.RegisterServerManagerStatus.DISPOSING);
460
465
  }
461
466
  /**
462
- * @param isOnline
463
- * @param outer_callback
467
+ * Handles the actual registration/unregistration request.
468
+ * It is designed to be interruptible by checking the state.
469
+ * @param isOnline - true for registration, false for unregistration
464
470
  * @private
465
471
  */
466
- _registerServer(isOnline, outer_callback) {
467
- (0, node_opcua_assert_1.assert)(typeof outer_callback === "function");
468
- debugLog("RegisterServerManager#_registerServer isOnline:", isOnline, "selectedEndpoint: ", this.selectedEndpoint?.endpointUrl);
469
- (0, node_opcua_assert_1.assert)(this.selectedEndpoint, "must have a selected endpoint => please call _establish_initial_connection");
472
+ async #_registerOrUnregisterServer(isOnline) {
473
+ const expectedState = isOnline ? i_register_server_manager_1.RegisterServerManagerStatus.REGISTERING : i_register_server_manager_1.RegisterServerManagerStatus.UNREGISTERING;
474
+ if (this.getState() !== expectedState) {
475
+ debugLog("RegisterServerManager#_registerServer: aborting due to state change");
476
+ return;
477
+ }
478
+ debugLog("RegisterServerManager#_registerServer isOnline:", isOnline);
479
+ (0, node_opcua_assert_1.assert)(this.selectedEndpoint, "must have a selected endpoint");
470
480
  (0, node_opcua_assert_1.assert)(this.server.serverType !== undefined, " must have a valid server Type");
471
- // construct connection
472
481
  const server = this.server;
473
482
  const selectedEndpoint = this.selectedEndpoint;
474
483
  if (!selectedEndpoint) {
475
- warningLog("Warning : cannot register server - no endpoint available");
476
- return outer_callback(new Error("Cannot registerServer"));
484
+ warningLog("Warning: cannot register server - no endpoint available");
485
+ // Do not rethrow here, let the caller handle it.
486
+ return;
477
487
  }
478
488
  server.serverCertificateManager.referenceCounter++;
479
489
  const applicationName = (0, node_opcua_client_1.coerceLocalizedText)(server.serverInfo.applicationName)?.text || undefined;
480
- const theStatus = isOnline ? RegisterServerManagerStatus.REGISTERING : RegisterServerManagerStatus.UNREGISTERING;
481
- if (theStatus === this.state) {
482
- warningLog(`Warning the server is already in the ${RegisterServerManagerStatus[theStatus]} state`);
483
- return outer_callback();
484
- }
485
- if (!(this.state === RegisterServerManagerStatus.INITIALIZING || this.state === RegisterServerManagerStatus.WAITING)) {
486
- warningLog("Warning : cannot register server - wrong state ", RegisterServerManagerStatus[this.state]);
487
- return outer_callback();
488
- }
489
- this._setState(theStatus);
490
- if (this._registration_client) {
491
- warningLog(`Warning there is already a registering/un-registering task taking place: ${RegisterServerManagerStatus[this.state]} state`);
492
- }
493
- const options = {
490
+ const prefix = `Client-${g_registeringClientCounter++}`;
491
+ const action = isOnline ? "registering" : "unregistering";
492
+ const ldsInfo = this.discoveryServerEndpointUrl;
493
+ const serverInfo = server.serverInfo.applicationUri;
494
+ const clientName = `${prefix} for server ${serverInfo} to LDS ${ldsInfo} for ${action}`;
495
+ const client = node_opcua_client_1.OPCUAClientBase.create({
496
+ clientName,
497
+ applicationName,
498
+ applicationUri: server.serverInfo.applicationUri,
499
+ connectionStrategy: no_retry_connectivity_strategy,
500
+ clientCertificateManager: server.serverCertificateManager,
494
501
  securityMode: selectedEndpoint.securityMode,
495
502
  securityPolicy: (0, node_opcua_secure_channel_1.coerceSecurityPolicy)(selectedEndpoint.securityPolicyUri),
496
503
  serverCertificate: selectedEndpoint.serverCertificate,
497
- clientCertificateManager: server.serverCertificateManager,
498
504
  certificateFile: server.certificateFile,
499
505
  privateKeyFile: server.privateKeyFile,
500
- // xx clientName: server.serverInfo.applicationUri!,
501
- applicationName,
502
- applicationUri: server.serverInfo.applicationUri,
503
- connectionStrategy: no_reconnect_connectivity_strategy,
504
- clientName: "server client to LDS " + RegisterServerManagerStatus[theStatus]
505
- };
506
- const client = node_opcua_client_1.OPCUAClientBase.create(options);
507
- const tmp = this._serverEndpoints;
508
- client._serverEndpoints = tmp;
506
+ });
507
+ client.on("backoff", (nbRetry, delay) => {
508
+ debugLog(client.clientCertificateManager, "backoff trying to connect to the LDS has failed", nbRetry, delay);
509
+ });
509
510
  this._registration_client = client;
510
- debugLog(" lds endpoint uri : ", selectedEndpoint.endpointUrl);
511
- debugLog(" securityMode : ", node_opcua_secure_channel_1.MessageSecurityMode[selectedEndpoint.securityMode]);
512
- debugLog(" securityPolicy : ", selectedEndpoint.securityPolicyUri);
513
- async_1.default.series([
514
- // establish_connection_with_lds
515
- (callback) => {
516
- client.connect(selectedEndpoint.endpointUrl, (err) => {
517
- debugLog("establish_connection_with_lds => err = ", err);
518
- if (err) {
519
- debugLog("RegisterServerManager#_registerServer connection to client has failed");
520
- debugLog("RegisterServerManager#_registerServer " +
521
- "=> please check that you server certificate is trusted by the LDS");
522
- warningLog("RegisterServer to the LDS has failed during secure connection " +
523
- "=> please check that you server certificate is trusted by the LDS.", "\nerr: " + err.message, "\nLDS endpoint :", selectedEndpoint.endpointUrl, "\nsecurity mode :", node_opcua_secure_channel_1.MessageSecurityMode[selectedEndpoint.securityMode], "\nsecurity policy :", (0, node_opcua_secure_channel_1.coerceSecurityPolicy)(selectedEndpoint.securityPolicyUri));
524
- // xx debugLog(options);
525
- client.disconnect(() => {
526
- this._registration_client = null;
527
- debugLog("RegisterServerManager#_registerServer client disconnected");
528
- callback( /* intentionally no error propagation*/);
529
- });
530
- }
531
- else {
532
- callback();
533
- }
534
- });
535
- },
536
- (callback) => {
537
- if (!this._registration_client) {
538
- callback();
539
- return;
511
+ debugLog("lds endpoint uri : ", selectedEndpoint.endpointUrl);
512
+ const state = isOnline ? "UnRegisterServer" : "RegisterServer";
513
+ try {
514
+ await client.connect(selectedEndpoint.endpointUrl);
515
+ // Check state again after connection is established
516
+ if (this.getState() === expectedState) {
517
+ try {
518
+ await sendRegisterServerRequest(server, client, isOnline);
540
519
  }
541
- sendRegisterServerRequest(this.server, client, isOnline, (err) => {
542
- callback( /* intentionally no error propagation*/);
543
- });
544
- },
545
- // close_connection_with_lds
546
- (callback) => {
547
- if (!this._registration_client) {
548
- callback();
549
- return;
520
+ catch (err) {
521
+ if (this.getState() !== expectedState) {
522
+ warningLog(`${state} '${this.server.serverInfo.applicationUri}' to the LDS has failed during secure connection to the LDS server`);
523
+ warningLog(chalk_1.default.red(" Error message:"), err.message); // Do not rethrow here, let the caller
524
+ }
550
525
  }
551
- client.disconnect(callback);
552
526
  }
553
- ], (err) => {
554
- if (!this._registration_client) {
555
- debugLog("RegisterServerManager#_registerServer end (isOnline", isOnline, ") has been interrupted");
556
- outer_callback();
557
- return;
527
+ else {
528
+ debugLog("RegisterServerManager#_registerServer: aborted ");
529
+ }
530
+ }
531
+ catch (err) {
532
+ if (this.getState() !== expectedState) {
533
+ warningLog(`${state} '${this.server.serverInfo.applicationUri}' cannot connect to LDS at endpoint ${client.clientName}, ${selectedEndpoint.endpointUrl} :`);
534
+ warningLog(chalk_1.default.red(" Error message:"), err.message);
558
535
  }
559
- this._registration_client.disconnect(() => {
560
- debugLog("RegisterServerManager#_registerServer end (isOnline", isOnline, ")");
536
+ }
537
+ finally {
538
+ if (this._registration_client) {
539
+ const tmp = this._registration_client;
561
540
  this._registration_client = null;
562
- outer_callback(err);
563
- });
564
- });
541
+ await tmp.disconnect();
542
+ }
543
+ }
565
544
  }
566
- _cancel_pending_client_if_any(callback) {
545
+ /**
546
+ * Cancels any pending client connections.
547
+ * This is crucial for a clean shutdown.
548
+ * @private
549
+ */
550
+ async #_cancel_pending_client_if_any() {
567
551
  debugLog("RegisterServerManager#_cancel_pending_client_if_any");
568
552
  if (this._registration_client) {
569
- debugLog("RegisterServerManager#_cancel_pending_client_if_any " + "=> wee need to disconnect _registration_client");
570
- this._registration_client.disconnect(() => {
571
- this._registration_client = null;
572
- this._cancel_pending_client_if_any(callback);
573
- });
553
+ const client = this._registration_client;
554
+ this._registration_client = null;
555
+ debugLog("RegisterServerManager#_cancel_pending_client_if_any "
556
+ + "=> wee need to disconnect_registration_client");
557
+ try {
558
+ await client.disconnect();
559
+ }
560
+ catch (err) {
561
+ warningLog("Error disconnecting registration client:", err.message);
562
+ }
563
+ await this.#_cancel_pending_client_if_any(); // Recursive call to ensure all are handled
574
564
  }
575
565
  else {
576
566
  debugLog("RegisterServerManager#_cancel_pending_client_if_any : done (nothing to do)");
577
- callback();
578
567
  }
579
568
  }
580
569
  }