node-opcua-server 2.51.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 (100) hide show
  1. package/.mocharc.yml +10 -0
  2. package/LICENSE +20 -0
  3. package/dist/base_server.d.ts +110 -0
  4. package/dist/base_server.js +476 -0
  5. package/dist/base_server.js.map +1 -0
  6. package/dist/factory.d.ts +10 -0
  7. package/dist/factory.js +24 -0
  8. package/dist/factory.js.map +1 -0
  9. package/dist/history_server_capabilities.d.ts +35 -0
  10. package/dist/history_server_capabilities.js +44 -0
  11. package/dist/history_server_capabilities.js.map +1 -0
  12. package/dist/i_channel_data.d.ts +13 -0
  13. package/dist/i_channel_data.js +3 -0
  14. package/dist/i_channel_data.js.map +1 -0
  15. package/dist/i_register_server_manager.d.ts +16 -0
  16. package/dist/i_register_server_manager.js +3 -0
  17. package/dist/i_register_server_manager.js.map +1 -0
  18. package/dist/i_server_side_publish_engine.d.ts +36 -0
  19. package/dist/i_server_side_publish_engine.js +50 -0
  20. package/dist/i_server_side_publish_engine.js.map +1 -0
  21. package/dist/i_socket_data.d.ts +11 -0
  22. package/dist/i_socket_data.js +3 -0
  23. package/dist/i_socket_data.js.map +1 -0
  24. package/dist/index.d.ts +14 -0
  25. package/dist/index.js +27 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/monitored_item.d.ts +173 -0
  28. package/dist/monitored_item.js +1006 -0
  29. package/dist/monitored_item.js.map +1 -0
  30. package/dist/node_sampler.d.ts +3 -0
  31. package/dist/node_sampler.js +76 -0
  32. package/dist/node_sampler.js.map +1 -0
  33. package/dist/opcua_server.d.ts +668 -0
  34. package/dist/opcua_server.js +2407 -0
  35. package/dist/opcua_server.js.map +1 -0
  36. package/dist/queue.d.ts +11 -0
  37. package/dist/queue.js +71 -0
  38. package/dist/queue.js.map +1 -0
  39. package/dist/register_server_manager.d.ts +92 -0
  40. package/dist/register_server_manager.js +574 -0
  41. package/dist/register_server_manager.js.map +1 -0
  42. package/dist/register_server_manager_hidden.d.ts +17 -0
  43. package/dist/register_server_manager_hidden.js +28 -0
  44. package/dist/register_server_manager_hidden.js.map +1 -0
  45. package/dist/register_server_manager_mdns_only.d.ts +19 -0
  46. package/dist/register_server_manager_mdns_only.js +58 -0
  47. package/dist/register_server_manager_mdns_only.js.map +1 -0
  48. package/dist/server_capabilities.d.ts +61 -0
  49. package/dist/server_capabilities.js +109 -0
  50. package/dist/server_capabilities.js.map +1 -0
  51. package/dist/server_end_point.d.ts +180 -0
  52. package/dist/server_end_point.js +825 -0
  53. package/dist/server_end_point.js.map +1 -0
  54. package/dist/server_engine.d.ts +311 -0
  55. package/dist/server_engine.js +1659 -0
  56. package/dist/server_engine.js.map +1 -0
  57. package/dist/server_publish_engine.d.ts +109 -0
  58. package/dist/server_publish_engine.js +531 -0
  59. package/dist/server_publish_engine.js.map +1 -0
  60. package/dist/server_publish_engine_for_orphan_subscriptions.d.ts +16 -0
  61. package/dist/server_publish_engine_for_orphan_subscriptions.js +50 -0
  62. package/dist/server_publish_engine_for_orphan_subscriptions.js.map +1 -0
  63. package/dist/server_session.d.ts +176 -0
  64. package/dist/server_session.js +734 -0
  65. package/dist/server_session.js.map +1 -0
  66. package/dist/server_subscription.d.ts +393 -0
  67. package/dist/server_subscription.js +1313 -0
  68. package/dist/server_subscription.js.map +1 -0
  69. package/dist/sessions_compatible_for_transfer.d.ts +2 -0
  70. package/dist/sessions_compatible_for_transfer.js +36 -0
  71. package/dist/sessions_compatible_for_transfer.js.map +1 -0
  72. package/dist/validate_filter.d.ts +5 -0
  73. package/dist/validate_filter.js +64 -0
  74. package/dist/validate_filter.js.map +1 -0
  75. package/package.json +88 -0
  76. package/source/base_server.ts +617 -0
  77. package/source/factory.ts +25 -0
  78. package/source/history_server_capabilities.ts +75 -0
  79. package/source/i_channel_data.ts +17 -0
  80. package/source/i_register_server_manager.ts +24 -0
  81. package/source/i_server_side_publish_engine.ts +77 -0
  82. package/source/i_socket_data.ts +11 -0
  83. package/source/index.ts +14 -0
  84. package/source/monitored_item.ts +1303 -0
  85. package/source/node_sampler.ts +82 -0
  86. package/source/opcua_server.ts +3742 -0
  87. package/source/queue.ts +73 -0
  88. package/source/register_server_manager.ts +744 -0
  89. package/source/register_server_manager_hidden.ts +33 -0
  90. package/source/register_server_manager_mdns_only.ts +69 -0
  91. package/source/server_capabilities.ts +177 -0
  92. package/source/server_end_point.ts +1182 -0
  93. package/source/server_engine.ts +2167 -0
  94. package/source/server_publish_engine.ts +657 -0
  95. package/source/server_publish_engine_for_orphan_subscriptions.ts +52 -0
  96. package/source/server_session.ts +931 -0
  97. package/source/server_subscription.ts +1792 -0
  98. package/source/sessions_compatible_for_transfer.ts +33 -0
  99. package/source/validate_filter.ts +86 -0
  100. package/test_helpers/create_certificates.js +1 -0
@@ -0,0 +1,744 @@
1
+ /**
2
+ * @module node-opcua-server
3
+ */
4
+ // tslint:disable:no-console
5
+ import * as async from "async";
6
+ import * as chalk from "chalk";
7
+ import { EventEmitter } from "events";
8
+
9
+ import { assert } from "node-opcua-assert";
10
+ import { ErrorCallback, UAString } from "node-opcua-basic-types";
11
+ import {
12
+ coerceLocalizedText,
13
+ LocalizedTextLike,
14
+ LocalizedTextOptions,
15
+ OPCUAClientBase,
16
+ OPCUAClientBaseOptions,
17
+ ResponseCallback
18
+ } from "node-opcua-client";
19
+ import { make_debugLog, checkDebugFlag, make_warningLog } from "node-opcua-debug";
20
+ import { resolveFullyQualifiedDomainName } from "node-opcua-hostname";
21
+ import { coerceSecurityPolicy, MessageSecurityMode, SecurityPolicy } from "node-opcua-secure-channel";
22
+ import {
23
+ RegisterServer2Request,
24
+ RegisterServer2Response,
25
+ RegisterServerRequest,
26
+ RegisterServerResponse
27
+ } from "node-opcua-service-discovery";
28
+ import { ApplicationType, EndpointDescription, MdnsDiscoveryConfiguration, RegisteredServerOptions } from "node-opcua-types";
29
+ import { IRegisterServerManager } from "./i_register_server_manager";
30
+ import { exploreCertificate } from "node-opcua-crypto";
31
+ import { OPCUACertificateManager } from "node-opcua-certificate-manager";
32
+
33
+ const doDebug = checkDebugFlag(__filename);
34
+ const debugLog = make_debugLog(__filename);
35
+ const warningLog = make_warningLog(__filename);
36
+
37
+ export enum RegisterServerManagerStatus {
38
+ INACTIVE = 1,
39
+ INITIALIZING = 2,
40
+ REGISTERING = 3,
41
+ WAITING = 4,
42
+ UNREGISTERING = 5
43
+ }
44
+
45
+ const g_DefaultRegistrationServerTimeout = 8 * 60 * 1000; // 8 minutes
46
+
47
+ function securityPolicyLevel(securityPolicy: UAString): number {
48
+ switch (securityPolicy) {
49
+ case SecurityPolicy.None:
50
+ return 0;
51
+ case SecurityPolicy.Basic128:
52
+ return 1;
53
+ case SecurityPolicy.Basic128Rsa15:
54
+ return 2;
55
+ case SecurityPolicy.Basic192:
56
+ return 3;
57
+ case SecurityPolicy.Basic192Rsa15:
58
+ return 4;
59
+ case SecurityPolicy.Basic256:
60
+ return 5;
61
+ case SecurityPolicy.Basic256Rsa15:
62
+ return 6;
63
+ case SecurityPolicy.Basic256Sha256:
64
+ return 7;
65
+ case SecurityPolicy.Aes128_Sha256_RsaOaep:
66
+ default:
67
+ return 0;
68
+ }
69
+ }
70
+
71
+ function sortEndpointBySecurityLevel(endpoints: EndpointDescription[]): EndpointDescription[] {
72
+ endpoints.sort((a: EndpointDescription, b: EndpointDescription) => {
73
+ if (a.securityMode === b.securityMode) {
74
+ if (a.securityPolicyUri === b.securityPolicyUri) {
75
+ const sa = a.securityLevel;
76
+ const sb = b.securityLevel;
77
+ return sa < sb ? 1 : sa > sb ? -1 : 0;
78
+ } else {
79
+ const sa = securityPolicyLevel(a.securityPolicyUri);
80
+ const sb = securityPolicyLevel(b.securityPolicyUri);
81
+ return sa < sb ? 1 : sa > sb ? -1 : 0;
82
+ }
83
+ } else {
84
+ return a.securityMode < b.securityMode ? 1 : 0;
85
+ }
86
+ });
87
+ return endpoints;
88
+ }
89
+
90
+ function findSecureEndpoint(endpoints: EndpointDescription[]): EndpointDescription | null {
91
+ // we only care about binary tcp transport endpoint
92
+ endpoints = endpoints.filter((e: EndpointDescription) => {
93
+ return e.transportProfileUri === "http://opcfoundation.org/UA-Profile/Transport/uatcp-uasc-uabinary";
94
+ });
95
+
96
+ endpoints = endpoints.filter((e: EndpointDescription) => {
97
+ return e.securityMode === MessageSecurityMode.SignAndEncrypt;
98
+ });
99
+
100
+ if (endpoints.length === 0) {
101
+ endpoints = endpoints.filter((e: EndpointDescription) => {
102
+ return e.securityMode === MessageSecurityMode.Sign;
103
+ });
104
+ }
105
+ if (endpoints.length === 0) {
106
+ endpoints = endpoints.filter((e: EndpointDescription) => {
107
+ return e.securityMode === MessageSecurityMode.None;
108
+ });
109
+ }
110
+ endpoints = sortEndpointBySecurityLevel(endpoints);
111
+ return endpoints[0];
112
+ }
113
+
114
+ function constructRegisteredServer(server: IPartialServer, isOnline: boolean): RegisteredServerOptions {
115
+ const discoveryUrls = server.getDiscoveryUrls();
116
+ assert(!isOnline || discoveryUrls.length >= 1, "expecting some discoveryUrls if we go online ....");
117
+
118
+ const info = exploreCertificate(server.getCertificate());
119
+ const commonName = info.tbsCertificate.subject.commonName!;
120
+
121
+ const serverUri = info.tbsCertificate.extensions?.subjectAltName.uniformResourceIdentifier[0];
122
+ // istanbul ignore next
123
+ if (serverUri !== server.serverInfo.applicationUri) {
124
+ warningLog(
125
+ chalk.yellow("Warning certificate uniformResourceIdentifier doesn't match serverInfo.applicationUri"),
126
+ "\n subjectKeyIdentifier : ",
127
+ info.tbsCertificate.extensions?.subjectKeyIdentifier,
128
+ "\n subjectAltName : ",
129
+ info.tbsCertificate.extensions?.subjectAltName,
130
+ "\n commonName : ",
131
+ info.tbsCertificate.subject.commonName!,
132
+ "\n serverInfo.applicationUri : ",
133
+ server.serverInfo.applicationUri
134
+ );
135
+ }
136
+
137
+ // istanbul ignore next
138
+ if (!server.serverInfo.applicationName.text) {
139
+ debugLog("warning: application name is missing");
140
+ }
141
+ // The globally unique identifier for the Server instance. The serverUri matches
142
+ // the applicationUri from the ApplicationDescription defined in 7.1.
143
+ const s = {
144
+ serverUri: server.serverInfo.applicationUri,
145
+
146
+ // The globally unique identifier for the Server product.
147
+ productUri: server.serverInfo.productUri,
148
+
149
+ serverNames: [
150
+ {
151
+ locale: "en-US",
152
+ text: server.serverInfo.applicationName.text
153
+ }
154
+ ],
155
+ serverType: server.serverType,
156
+
157
+ discoveryUrls,
158
+ gatewayServerUri: null,
159
+ isOnline,
160
+ semaphoreFilePath: null
161
+ };
162
+ return s;
163
+ }
164
+ function constructRegisterServerRequest(serverB: IPartialServer, isOnline: boolean): RegisterServerRequest {
165
+ const server = constructRegisteredServer(serverB, isOnline);
166
+ return new RegisterServerRequest({
167
+ server
168
+ });
169
+ }
170
+
171
+ function constructRegisterServer2Request(serverB: IPartialServer, isOnline: boolean): RegisterServer2Request {
172
+ const server = constructRegisteredServer(serverB, isOnline);
173
+
174
+ return new RegisterServer2Request({
175
+ discoveryConfiguration: [
176
+ new MdnsDiscoveryConfiguration({
177
+ mdnsServerName: serverB.serverInfo.applicationUri,
178
+ serverCapabilities: serverB.capabilitiesForMDNS
179
+ })
180
+ ],
181
+ server
182
+ });
183
+ }
184
+
185
+ const no_reconnect_connectivity_strategy = {
186
+ initialDelay: 2000,
187
+ maxDelay: 50000,
188
+ maxRetry: 1, // NO RETRY !!!
189
+ randomisationFactor: 0
190
+ };
191
+ const infinite_connectivity_strategy = {
192
+ initialDelay: 2000,
193
+ maxDelay: 50000,
194
+ maxRetry: 10000000,
195
+ randomisationFactor: 0
196
+ };
197
+
198
+ interface ClientBaseEx extends OPCUAClientBase {
199
+ _serverEndpoints: EndpointDescription[];
200
+
201
+ performMessageTransaction(request: RegisterServer2Request, callback: ResponseCallback<RegisterServer2Response>): void;
202
+ performMessageTransaction(request: RegisterServerRequest, callback: ResponseCallback<RegisterServerResponse>): void;
203
+ }
204
+
205
+ function sendRegisterServerRequest(server: IPartialServer, client: ClientBaseEx, isOnline: boolean, callback: ErrorCallback) {
206
+ // try to send a RegisterServer2Request
207
+ const request = constructRegisterServer2Request(server, isOnline);
208
+
209
+ client.performMessageTransaction(request, (err: Error | null, response?: RegisterServer2Response) => {
210
+ if (!err) {
211
+ // RegisterServerResponse
212
+ debugLog("RegisterServerManager#_registerServer sendRegisterServer2Request has succeeded (isOnline", isOnline, ")");
213
+ assert(response instanceof RegisterServer2Response);
214
+ callback(err!);
215
+ } else {
216
+ debugLog("RegisterServerManager#_registerServer sendRegisterServer2Request has failed " + "(isOnline", isOnline, ")");
217
+ debugLog("RegisterServerManager#_registerServer" + " falling back to using sendRegisterServerRequest instead");
218
+ // fall back to
219
+ const request1 = constructRegisterServerRequest(server, isOnline);
220
+ client.performMessageTransaction(request1, (err1: Error | null, response1?: RegisterServerResponse) => {
221
+ if (!err1) {
222
+ debugLog(
223
+ "RegisterServerManager#_registerServer sendRegisterServerRequest " + "has succeeded (isOnline",
224
+ isOnline,
225
+ ")"
226
+ );
227
+ assert(response1 instanceof RegisterServerResponse);
228
+ } else {
229
+ debugLog(
230
+ "RegisterServerManager#_registerServer sendRegisterServerRequest " + "has failed (isOnline",
231
+ isOnline,
232
+ ")"
233
+ );
234
+ }
235
+ callback(err1!);
236
+ });
237
+ }
238
+ });
239
+ }
240
+
241
+ export interface IPartialServer {
242
+ serverCertificateManager: OPCUACertificateManager;
243
+ certificateFile: string;
244
+ privateKeyFile: string;
245
+ serverType: ApplicationType;
246
+ serverInfo: {
247
+ applicationUri: UAString;
248
+ applicationName: LocalizedTextOptions;
249
+ productUri: UAString;
250
+ };
251
+ capabilitiesForMDNS: string[];
252
+ getDiscoveryUrls(): string[];
253
+ getCertificate(): Buffer;
254
+ }
255
+ export interface RegisterServerManagerOptions {
256
+ server: IPartialServer;
257
+ discoveryServerEndpointUrl: string;
258
+ }
259
+ /**
260
+ * RegisterServerManager is responsible to Register an opcua server on a LDS or LDS-ME server
261
+ * This class takes in charge :
262
+ * - the initial registration of a server
263
+ * - the regular registration renewal (every 8 minutes or so ...)
264
+ * - dealing with cases where LDS is not up and running when server starts.
265
+ * ( in this case the connection will be continuously attempted using the infinite
266
+ * back-off strategy
267
+ * - the un-registration of the server ( during shutdown for instance)
268
+ *
269
+ * Events:
270
+ *
271
+ * Emitted when the server is trying to registered the LDS
272
+ * but when the connection to the lds has failed
273
+ * serverRegistrationPending is sent when the backoff signal of the
274
+ * connection process is rained
275
+ * @event serverRegistrationPending
276
+ *
277
+ * emitted when the server is successfully registered to the LDS
278
+ * @event serverRegistered
279
+ *
280
+ * emitted when the server has successfully renewed its registration to the LDS
281
+ * @event serverRegistrationRenewed
282
+ *
283
+ * emitted when the server is successfully unregistered to the LDS
284
+ * ( for instance during shutdown)
285
+ * @event serverUnregistered
286
+ *
287
+ *
288
+ * (LDS => Local Discovery Server)
289
+ * @param options
290
+ * @param options.server {OPCUAServer}
291
+ * @param options.discoveryServerEndpointUrl {String}
292
+ * @constructor
293
+ */
294
+ export class RegisterServerManager extends EventEmitter implements IRegisterServerManager {
295
+ public discoveryServerEndpointUrl: string;
296
+ public timeout: number;
297
+
298
+ private server: IPartialServer | null;
299
+ private _registrationTimerId: NodeJS.Timer | null;
300
+ private state: RegisterServerManagerStatus = RegisterServerManagerStatus.INACTIVE;
301
+ private _registration_client: OPCUAClientBase | null = null;
302
+ private selectedEndpoint?: EndpointDescription;
303
+ private _serverEndpoints: EndpointDescription[] = [];
304
+
305
+ constructor(options: RegisterServerManagerOptions) {
306
+ super();
307
+
308
+ this.server = options.server;
309
+ this._setState(RegisterServerManagerStatus.INACTIVE);
310
+ this.timeout = g_DefaultRegistrationServerTimeout;
311
+ this.discoveryServerEndpointUrl = options.discoveryServerEndpointUrl || "opc.tcp://localhost:4840";
312
+
313
+ assert(typeof this.discoveryServerEndpointUrl === "string");
314
+ this._registrationTimerId = null;
315
+ }
316
+
317
+ public dispose() {
318
+ this.server = null;
319
+ debugLog("RegisterServerManager#dispose", this.state.toString());
320
+ assert(this.state === RegisterServerManagerStatus.INACTIVE);
321
+ assert(this._registrationTimerId === null, "stop has not been called");
322
+ this.removeAllListeners();
323
+ }
324
+
325
+ public _emitEvent(eventName: string) {
326
+ setImmediate(() => {
327
+ this.emit(eventName);
328
+ });
329
+ }
330
+
331
+ public _setState(status: RegisterServerManagerStatus) {
332
+ const previousState = this.state || RegisterServerManagerStatus.INACTIVE;
333
+ debugLog(
334
+ "RegisterServerManager#setState : ",
335
+ RegisterServerManagerStatus[previousState],
336
+ " => ",
337
+ RegisterServerManagerStatus[status]
338
+ );
339
+ this.state = status;
340
+ }
341
+
342
+ public start(callback: ErrorCallback) {
343
+ debugLog("RegisterServerManager#start");
344
+ if (this.state !== RegisterServerManagerStatus.INACTIVE) {
345
+ return callback(new Error("RegisterServer process already started")); // already started
346
+ }
347
+
348
+ this.discoveryServerEndpointUrl = resolveFullyQualifiedDomainName(this.discoveryServerEndpointUrl);
349
+
350
+ // perform initial registration + automatic renewal
351
+ this._establish_initial_connection((err?: Error | null) => {
352
+ if (err) {
353
+ debugLog("RegisterServerManager#start => _establish_initial_connection has failed");
354
+ return callback(err);
355
+ }
356
+ if (this.state !== RegisterServerManagerStatus.INITIALIZING) {
357
+ debugLog("RegisterServerManager#start => _establish_initial_connection has failed");
358
+ return callback();
359
+ }
360
+
361
+ this._registerServer(true, (err1?: Error | null) => {
362
+ if (this.state !== RegisterServerManagerStatus.REGISTERING) {
363
+ debugLog("RegisterServerManager#start )=> Registration has been cancelled");
364
+ return callback(new Error("Registration has been cancelled"));
365
+ }
366
+
367
+ if (err1) {
368
+ warningLog(
369
+ "RegisterServerManager#start - registering server has failed ! \n" +
370
+ "please check that your server certificate is accepted by the LDS"
371
+ );
372
+ this._setState(RegisterServerManagerStatus.INACTIVE);
373
+ this._emitEvent("serverRegistrationFailure");
374
+ } else {
375
+ this._emitEvent("serverRegistered");
376
+ this._setState(RegisterServerManagerStatus.WAITING);
377
+ this._trigger_next();
378
+ }
379
+ callback();
380
+ });
381
+ });
382
+ }
383
+
384
+ private _establish_initial_connection(outer_callback: ErrorCallback) {
385
+ /* istanbul ignore next */
386
+ if (!this.server) {
387
+ throw new Error("Internal Error");
388
+ }
389
+ debugLog("RegisterServerManager#_establish_initial_connection");
390
+
391
+ assert(!this._registration_client);
392
+ assert(typeof this.discoveryServerEndpointUrl === "string");
393
+ assert(this.state === RegisterServerManagerStatus.INACTIVE);
394
+ this._setState(RegisterServerManagerStatus.INITIALIZING);
395
+ this.selectedEndpoint = undefined;
396
+
397
+ const applicationName = coerceLocalizedText(this.server.serverInfo.applicationName!)?.text || undefined;
398
+
399
+ // Retry Strategy must be set
400
+ this.server.serverCertificateManager.referenceCounter++;
401
+ const registrationClient = OPCUAClientBase.create({
402
+ clientName: this.server.serverInfo.applicationUri!,
403
+
404
+ applicationName,
405
+
406
+ applicationUri: this.server.serverInfo.applicationUri!,
407
+
408
+ connectionStrategy: infinite_connectivity_strategy,
409
+
410
+ clientCertificateManager: this.server.serverCertificateManager,
411
+
412
+ certificateFile: this.server.certificateFile,
413
+ privateKeyFile: this.server.privateKeyFile
414
+ }) as ClientBaseEx;
415
+
416
+ this._registration_client = registrationClient;
417
+
418
+ registrationClient.on("backoff", (nbRetry: number, delay: number) => {
419
+ debugLog("RegisterServerManager - received backoff");
420
+ warningLog(
421
+ chalk.bgWhite.cyan("contacting discovery server backoff "),
422
+ this.discoveryServerEndpointUrl,
423
+ " attempt #",
424
+ nbRetry,
425
+ " retrying in ",
426
+ delay / 1000.0,
427
+ " seconds"
428
+ );
429
+ this._emitEvent("serverRegistrationPending");
430
+ });
431
+
432
+ async.series(
433
+ [
434
+ // do_initial_connection_with_discovery_server
435
+ (callback: ErrorCallback) => {
436
+ registrationClient.connect(this.discoveryServerEndpointUrl, (err?: Error) => {
437
+ if (err) {
438
+ debugLog(
439
+ "RegisterServerManager#_establish_initial_connection " + ": initial connection to server has failed"
440
+ );
441
+ // xx debugLog(err);
442
+ }
443
+ return callback(err);
444
+ });
445
+ },
446
+
447
+ // getEndpoints_on_discovery_server
448
+ (callback: ErrorCallback) => {
449
+ registrationClient.getEndpoints((err: Error | null, endpoints?: EndpointDescription[]) => {
450
+ if (!err) {
451
+ const endpoint = findSecureEndpoint(endpoints!);
452
+
453
+ if (!endpoint) {
454
+ throw new Error("Cannot find Secure endpoint");
455
+ }
456
+
457
+ if (endpoint.serverCertificate) {
458
+ assert(endpoint.serverCertificate);
459
+ this.selectedEndpoint = endpoint;
460
+ } else {
461
+ this.selectedEndpoint = undefined;
462
+ }
463
+ } else {
464
+ debugLog("RegisterServerManager#_establish_initial_connection " + ": getEndpointsRequest has failed");
465
+ debugLog(err);
466
+ }
467
+ callback(err!);
468
+ });
469
+ },
470
+ // function closing_discovery_server_connection
471
+ (callback: ErrorCallback) => {
472
+ this._serverEndpoints = registrationClient._serverEndpoints;
473
+
474
+ registrationClient.disconnect((err?: Error) => {
475
+ this._registration_client = null;
476
+ callback(err);
477
+ });
478
+ },
479
+ // function wait_a_little_bit
480
+ (callback: ErrorCallback) => {
481
+ setTimeout(callback, 100);
482
+ }
483
+ ],
484
+ (err?: Error | null) => {
485
+ debugLog("-------------------------------", !!err);
486
+
487
+ if (this.state !== RegisterServerManagerStatus.INITIALIZING) {
488
+ debugLog("RegisterServerManager#_establish_initial_connection has been interrupted ", RegisterServerManagerStatus[this.state]);
489
+ this._setState(RegisterServerManagerStatus.INACTIVE);
490
+ if (this._registration_client) {
491
+ this._registration_client.disconnect((err2?: Error) => {
492
+ this._registration_client = null;
493
+ outer_callback(new Error("Initialization has been canceled"));
494
+ });
495
+ } else {
496
+ outer_callback(new Error("Initialization has been canceled"));
497
+ }
498
+ return;
499
+ }
500
+ if (err) {
501
+ this._setState(RegisterServerManagerStatus.INACTIVE);
502
+ if (this._registration_client) {
503
+ this._registration_client.disconnect((err1?: Error) => {
504
+ this._registration_client = null;
505
+ debugLog("#######", !!err1);
506
+ outer_callback(err);
507
+ });
508
+ return;
509
+ }
510
+ }
511
+ outer_callback();
512
+ }
513
+ );
514
+ }
515
+
516
+ public _trigger_next() {
517
+ assert(!this._registrationTimerId);
518
+ assert(this.state === RegisterServerManagerStatus.WAITING);
519
+ // from spec 1.04 part 4:
520
+ // The registration process is designed to be platform independent, robust and able to minimize
521
+ // problems created by configuration errors. For that reason, Servers shall register themselves more
522
+ // than once.
523
+ // Under normal conditions, manually launched Servers shall periodically register with the Discovery
524
+ // Server as long as they are able to receive connections from Clients. If a Server goes offline then it
525
+ // shall register itself once more and indicate that it is going offline. The registration frequency
526
+ // should be configurable; however, the maximum is 10 minutes. If an error occurs during registration
527
+ // (e.g. the Discovery Server is not running) then the Server shall periodically re-attempt registration.
528
+ // The frequency of these attempts should start at 1 second but gradually increase until the
529
+ // registration frequency is the same as what it would be if no errors occurred. The recommended
530
+ // approach would be to double the period of each attempt until reaching the maximum.
531
+ // When an automatically launched Server (or its install program) registers with the Discovery Server
532
+ // it shall provide a path to a semaphore file which the Discovery Server can use to determine if the
533
+ // Server has been uninstalled from the machine. The Discovery Server shall have read access to
534
+ // the file system that contains the file
535
+
536
+ // install a registration
537
+ debugLog(
538
+ "RegisterServerManager#_trigger_next " + ": installing timeout to perform registerServer renewal (timeout =",
539
+ this.timeout,
540
+ ")"
541
+ );
542
+
543
+ this._registrationTimerId = setTimeout(() => {
544
+ if (!this._registrationTimerId) {
545
+ debugLog("RegisterServerManager => cancelling re registration");
546
+ return;
547
+ }
548
+ this._registrationTimerId = null;
549
+
550
+ debugLog("RegisterServerManager#_trigger_next : renewing RegisterServer");
551
+ this._registerServer(true, (err?: Error | null) => {
552
+ if (
553
+ this.state !== RegisterServerManagerStatus.INACTIVE &&
554
+ this.state !== RegisterServerManagerStatus.UNREGISTERING
555
+ ) {
556
+ debugLog("RegisterServerManager#_trigger_next : renewed !", err);
557
+ this._setState(RegisterServerManagerStatus.WAITING);
558
+ this._emitEvent("serverRegistrationRenewed");
559
+ this._trigger_next();
560
+ }
561
+ });
562
+ }, this.timeout);
563
+ }
564
+
565
+ public stop(callback: ErrorCallback) {
566
+ debugLog("RegisterServerManager#stop");
567
+
568
+ if (this._registrationTimerId) {
569
+ debugLog("RegisterServerManager#stop :clearing timeout");
570
+ clearTimeout(this._registrationTimerId);
571
+ this._registrationTimerId = null;
572
+ }
573
+
574
+ this._cancel_pending_client_if_any(() => {
575
+ debugLog("RegisterServerManager#stop _cancel_pending_client_if_any done ", this.state);
576
+
577
+ if (!this.selectedEndpoint || this.state === RegisterServerManagerStatus.INACTIVE) {
578
+ this.state = RegisterServerManagerStatus.INACTIVE;
579
+ assert(this._registrationTimerId === null);
580
+ return callback();
581
+ }
582
+ this._registerServer(false, () => {
583
+ this._setState(RegisterServerManagerStatus.INACTIVE);
584
+ this._emitEvent("serverUnregistered");
585
+ callback();
586
+ });
587
+ });
588
+ }
589
+
590
+ /**
591
+ * @param isOnline
592
+ * @param outer_callback
593
+ * @private
594
+ */
595
+ public _registerServer(isOnline: boolean, outer_callback: ErrorCallback) {
596
+ assert(typeof outer_callback === "function");
597
+
598
+ debugLog(
599
+ "RegisterServerManager#_registerServer isOnline:",
600
+ isOnline,
601
+ "selectedEndpoint: ",
602
+ this.selectedEndpoint?.endpointUrl
603
+ );
604
+
605
+ assert(this.selectedEndpoint, "must have a selected endpoint => please call _establish_initial_connection");
606
+
607
+ assert(this.server!.serverType !== undefined, " must have a valid server Type");
608
+
609
+ // construct connection
610
+ const server = this.server!;
611
+ const selectedEndpoint = this.selectedEndpoint;
612
+
613
+ if (!selectedEndpoint) {
614
+ warningLog("Warning : cannot register server - no endpoint available");
615
+ return outer_callback(new Error("Cannot registerServer"));
616
+ }
617
+
618
+ server.serverCertificateManager.referenceCounter++;
619
+
620
+ const applicationName: string | undefined = coerceLocalizedText(server.serverInfo.applicationName!)?.text || undefined;
621
+
622
+ const theStatus = isOnline ? RegisterServerManagerStatus.REGISTERING : RegisterServerManagerStatus.UNREGISTERING;
623
+
624
+ if (theStatus === this.state) {
625
+ warningLog(`Warning the server is already in the ${RegisterServerManagerStatus[theStatus]} state`);
626
+ return outer_callback();
627
+ }
628
+ assert(this.state === RegisterServerManagerStatus.INITIALIZING || this.state === RegisterServerManagerStatus.WAITING);
629
+
630
+ this._setState(theStatus);
631
+
632
+ if (this._registration_client) {
633
+ warningLog(`Warning there is already a registering/unregistering task taking place: ${RegisterServerManagerStatus[this.state]} state`);
634
+ }
635
+
636
+ const options: OPCUAClientBaseOptions = {
637
+ securityMode: selectedEndpoint.securityMode,
638
+ securityPolicy: coerceSecurityPolicy(selectedEndpoint.securityPolicyUri),
639
+ serverCertificate: selectedEndpoint.serverCertificate,
640
+
641
+ clientCertificateManager: server.serverCertificateManager,
642
+
643
+ certificateFile: server.certificateFile,
644
+ privateKeyFile: server.privateKeyFile,
645
+
646
+ // xx clientName: server.serverInfo.applicationUri!,
647
+
648
+ applicationName,
649
+
650
+ applicationUri: server.serverInfo.applicationUri!,
651
+
652
+ connectionStrategy: no_reconnect_connectivity_strategy,
653
+
654
+ clientName: "server client to LDS " + RegisterServerManagerStatus[theStatus],
655
+ };
656
+
657
+ const client = OPCUAClientBase.create(options) as ClientBaseEx;
658
+
659
+ const tmp = this._serverEndpoints;
660
+ client._serverEndpoints = tmp;
661
+ this._registration_client = client;
662
+
663
+ debugLog(" lds endpoint uri : ", selectedEndpoint.endpointUrl);
664
+ debugLog(" securityMode : ", MessageSecurityMode[selectedEndpoint.securityMode]);
665
+ debugLog(" securityPolicy : ", selectedEndpoint.securityPolicyUri);
666
+
667
+ async.series(
668
+ [
669
+ // establish_connection_with_lds
670
+ (callback: ErrorCallback) => {
671
+ client.connect(selectedEndpoint?.endpointUrl!, (err?: Error) => {
672
+ debugLog("establish_connection_with_lds => err = ", err);
673
+ if (err) {
674
+ debugLog("RegisterServerManager#_registerServer connection to client has failed");
675
+ debugLog(
676
+ "RegisterServerManager#_registerServer " +
677
+ "=> please check that you server certificate is trusted by the LDS"
678
+ );
679
+ warningLog(
680
+ "RegisterServer to the LDS has failed during secure connection " +
681
+ "=> please check that you server certificate is trusted by the LDS.",
682
+ "\nerr: " + err.message,
683
+ "\nLDS endpoint :",
684
+ selectedEndpoint?.endpointUrl!,
685
+ "\nsecurity mode :",
686
+ MessageSecurityMode[selectedEndpoint.securityMode],
687
+ "\nsecurity policy :",
688
+ coerceSecurityPolicy(selectedEndpoint.securityPolicyUri)
689
+ );
690
+ // xx debugLog(options);
691
+ client.disconnect(() => {
692
+ this._registration_client = null;
693
+ debugLog("RegisterServerManager#_registerServer client disconnected");
694
+ callback(/* intentionally no error propagation*/);
695
+ });
696
+ } else {
697
+ callback();
698
+ }
699
+ });
700
+ },
701
+ (callback: ErrorCallback) => {
702
+ if (!this._registration_client) { callback(); return; }
703
+ sendRegisterServerRequest(this.server!, client as ClientBaseEx, isOnline, (err?: Error | null) => {
704
+ callback(/* intentionally no error propagation*/);
705
+ });
706
+ },
707
+ // close_connection_with_lds
708
+ (callback: ErrorCallback) => {
709
+ if (!this._registration_client) { callback(); return; }
710
+ client.disconnect(callback);
711
+ }
712
+ ],
713
+ (err?: Error | null) => {
714
+ if (!this._registration_client) {
715
+ debugLog("RegisterServerManager#_registerServer end (isOnline", isOnline, ") has been interrupted");
716
+ outer_callback();
717
+ return;
718
+ }
719
+ this._registration_client.disconnect(() => {
720
+ debugLog("RegisterServerManager#_registerServer end (isOnline", isOnline, ")");
721
+ this._registration_client = null;
722
+ outer_callback(err!);
723
+ });
724
+
725
+ }
726
+ );
727
+ }
728
+
729
+ private _cancel_pending_client_if_any(callback: () => void) {
730
+ debugLog("RegisterServerManager#_cancel_pending_client_if_any");
731
+
732
+ if (this._registration_client) {
733
+ debugLog("RegisterServerManager#_cancel_pending_client_if_any " + "=> wee need to disconnect _registration_client");
734
+
735
+ this._registration_client.disconnect(() => {
736
+ this._registration_client = null;
737
+ this._cancel_pending_client_if_any(callback);
738
+ });
739
+ } else {
740
+ debugLog("RegisterServerManager#_cancel_pending_client_if_any : done");
741
+ callback();
742
+ }
743
+ }
744
+ }