@vitormnm/node-red-simple-opcua 1.6.2 → 1.7.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 (42) hide show
  1. package/README.md +89 -136
  2. package/client/lib/opcua-client-browser.js +238 -10
  3. package/client/lib/opcua-client-method-service.js +1 -1
  4. package/client/lib/opcua-client-subscription-service.js +0 -2
  5. package/client/opcua-client-config.html +118 -1
  6. package/client/opcua-client-config.js +74 -8
  7. package/client/opcua-client-help.html +6 -0
  8. package/client/opcua-client-utils.js +34 -10
  9. package/client/opcua-client.html +7 -0
  10. package/client/opcua-client.js +97 -1
  11. package/examples/flows_simple_opc.json +1 -1
  12. package/package.json +1 -1
  13. package/server/lib/opcua-address-space-alarm.js +11 -5
  14. package/server/lib/opcua-address-space-builder.js +65 -15
  15. package/server/lib/opcua-config.js +81 -23
  16. package/server/lib/opcua-server-events-child.js +1 -1
  17. package/server/lib/opcua-server-runtime-child.js +429 -59
  18. package/server/lib/opcua-server-runtime.js +49 -5
  19. package/server/lib/opcua-server-status-child.js +14 -14
  20. package/server/nodered/simple_opcua/server/certificates/mutex +0 -0
  21. package/server/nodered/simple_opcua/server/certificates/own/certs/server_selfsigned_cert_2048.pem +25 -0
  22. package/server/nodered/simple_opcua/server/certificates/own/certs/server_selfsigned_cert_2048.pem.mutex +0 -0
  23. package/server/nodered/simple_opcua/server/certificates/own/openssl.cnf +72 -0
  24. package/server/nodered/simple_opcua/server/certificates/own/private/private_key.pem +28 -0
  25. package/server/nodered/simple_opcua/server/certificates/trusted/certs/NodeOPCUA-Client@tuf[c5a9e20a8b680cdff76aaf0165bb3c9318da37a5].pem +25 -0
  26. package/server/nodered/simple_opcua/server/myServer1/mutex +0 -0
  27. package/server/nodered/simple_opcua/server/myServer1/own/certs/server_selfsigned_cert_2048.pem +25 -0
  28. package/server/nodered/simple_opcua/server/myServer1/own/certs/server_selfsigned_cert_2048.pem.mutex +0 -0
  29. package/server/nodered/simple_opcua/server/myServer1/own/openssl.cnf +72 -0
  30. package/server/nodered/simple_opcua/server/myServer1/own/private/private_key.pem +28 -0
  31. package/server/nodered/simple_opcua/server/myServer1/trusted/certs/NodeOPCUA-Client@tuf[91e520c64ff891c67168f08a46dd194071e15dae].pem +25 -0
  32. package/server/nodered/simple_opcua/server/myServer1/trusted/certs/NodeOPCUA-Client@tuf[98ae95da627cea4c500753c319161a3554ee38d7].pem +25 -0
  33. package/server/nodered/simple_opcua/server/myServer1/trusted/certs/NodeOPCUA-Client@tuf[aef8d7a1cfba13d84189a0bcf1694208fc51a7f9].pem +25 -0
  34. package/server/nodered/simple_opcua/server/myServer1/trusted/certs/NodeOPCUA-Client@tuf[c5a9e20a8b680cdff76aaf0165bb3c9318da37a5].pem +25 -0
  35. package/server/nodered/simple_opcua/server/myServer1/trusted/certs/NodeOPCUA-Client@tuf[ebdf9acf1d02e347917a14108d3144799c638ea3].pem +25 -0
  36. package/server/opcua-server-io.html +76 -0
  37. package/server/opcua-server-io.js +135 -23
  38. package/server/opcua-server.css +52 -0
  39. package/server/opcua-server.html +166 -44
  40. package/server/opcua-server.js +142 -7
  41. package/server/view/opcua-server.css +89 -6
  42. package/server/view/opcua-server.js +523 -42
@@ -193,15 +193,18 @@ class OpcUaServerProcess {
193
193
  const target = msg && msg.opcuaServerIo ? msg.opcuaServerIo : {};
194
194
  const identifierType = this.resolveIdentifierType(target);
195
195
 
196
- let result = {}
196
+ let result = {};
197
+ let readArrayResults = null;
197
198
 
198
199
  if (Array.isArray(payload)) {
199
200
  if (!payload.length) {
200
201
  throw new Error("msg.payload array does not contain any items");
201
202
  }
202
203
 
204
+ readArrayResults = payload.map((item) => this.readPayloadItem(identifierType, item));
205
+
203
206
  result = {
204
- payload: payload.map((item) => this.readPayloadItem(identifierType, item)),
207
+ payload: readArrayResults,
205
208
  identifiers: payload.map((item) => this.resolvePayloadItemIdentifier(item))
206
209
  };
207
210
  } else if (payload && typeof payload === "object" && !Array.isArray(payload)) {
@@ -225,9 +228,18 @@ class OpcUaServerProcess {
225
228
  };
226
229
  } else {
227
230
  const identifier = this.resolveIdentifier(target);
231
+ let directValue = null;
232
+ let directError = null;
233
+ try {
234
+ directValue = server.readValue(identifierType, identifier);
235
+ } catch (e) {
236
+ directValue = null;
237
+ directError = { identifier, message: e.message || String(e) };
238
+ }
228
239
  result = {
229
- payload: server.readValue(identifierType, identifier),
230
- identifiers: [identifier]
240
+ payload: directValue,
241
+ identifiers: [identifier],
242
+ directError
231
243
  };
232
244
 
233
245
  }
@@ -245,28 +257,73 @@ class OpcUaServerProcess {
245
257
 
246
258
 
247
259
 
248
- process.send({
249
- type: "send",
250
- data: msg,
251
- nodeId: nodeId
252
- });
260
+ let hasFailures = false;
261
+ if (readArrayResults) {
262
+ const failed = readArrayResults.filter(item => item && item.status !== "Good");
263
+ if (failed.length) {
264
+ hasFailures = true;
265
+ }
266
+ }
267
+ if (result.directError) {
268
+ hasFailures = true;
269
+ }
270
+
271
+ if (!hasFailures) {
272
+ process.send({
273
+ type: "send",
274
+ data: msg,
275
+ nodeId: nodeId
276
+ });
277
+ }
253
278
 
254
279
  process.send({
255
280
  type: "status",
256
281
  data: {
257
- fill: "green",
282
+ fill: hasFailures ? (result.directError ? "red" : "yellow") : "green",
258
283
  shape: "dot",
259
- text: result.identifiers.length > 1 ? "read " + result.identifiers.length + " tags" : "read " + result.identifiers[0]
284
+ text: hasFailures
285
+ ? (result.directError ? "read failed" : "partial read failed")
286
+ : (result.identifiers.length > 1 ? "read " + result.identifiers.length + " tags" : "read " + result.identifiers[0])
260
287
  },
261
288
  nodeId: nodeId
262
289
  });
263
290
 
291
+ // Emit a partialError for items that could not be read (array-of-objects mode only)
292
+ if (readArrayResults) {
293
+ const failed = readArrayResults
294
+ .filter(item => item && item.status !== "Good")
295
+ .map(item => ({ name: item.name, path: item.path, status: item.status }));
296
+
297
+ if (failed.length) {
298
+ process.send({
299
+ type: "partialError",
300
+ error: "Some tags could not be read: " + failed.map(f => f.path).join(", "),
301
+ failed: failed,
302
+ originalMsg: msg,
303
+ nodeId: nodeId
304
+ });
305
+ }
306
+ }
307
+
308
+ // Emit a partialError for a direct single-tag read failure
309
+ if (result.directError) {
310
+ const { identifier, message } = result.directError;
311
+ process.send({
312
+ type: "partialError",
313
+ error: "Some tags could not be read: " + identifier,
314
+ failed: [{ name: "", path: identifier, status: message }],
315
+ originalMsg: msg,
316
+ nodeId: nodeId
317
+ });
318
+ }
319
+
264
320
  } catch (error) {
265
321
 
266
322
  process.send({
267
323
  type: "error",
268
324
  data: { fill: "red", shape: "ring", text: "failed read" },
269
325
  error: error.message,
326
+ originalMsg: msg,
270
327
  nodeId: nodeId
271
328
  });
272
329
  }
@@ -321,6 +378,7 @@ class OpcUaServerProcess {
321
378
  type: "error",
322
379
  data: { fill: "red", shape: "ring", text: "failed write" },
323
380
  error: error.message,
381
+ originalMsg: msg,
324
382
  nodeId: nodeId
325
383
  });
326
384
  }
@@ -335,6 +393,7 @@ class OpcUaServerProcess {
335
393
  try {
336
394
  let writtenPaths = null;
337
395
  let payload = msg ? msg.payload : undefined;
396
+ let directError = null;
338
397
 
339
398
  const target = msg && msg.opcuaServerIo ? msg.opcuaServerIo : {};
340
399
  const identifierType = this.resolveIdentifierType(target);
@@ -365,14 +424,18 @@ class OpcUaServerProcess {
365
424
  if (Buffer.isBuffer(payload) || payload instanceof Uint8Array) {
366
425
 
367
426
  const identifier = this.resolveIdentifier(target);
368
-
369
- this.node.writeValue(
370
- identifierType,
371
- identifier,
372
- Buffer.isBuffer(payload)
373
- ? payload
374
- : Buffer.from(payload)
375
- );
427
+ try {
428
+ this.node.writeValue(
429
+ identifierType,
430
+ identifier,
431
+ Buffer.isBuffer(payload)
432
+ ? payload
433
+ : Buffer.from(payload)
434
+ );
435
+ } catch (e) {
436
+ directError = { identifier, message: e.message || String(e) };
437
+ msg.payload = null;
438
+ }
376
439
 
377
440
  writtenPaths = [identifier];
378
441
  }
@@ -384,14 +447,18 @@ class OpcUaServerProcess {
384
447
  ) {
385
448
 
386
449
  const identifier = this.resolveIdentifier(target);
387
-
388
- this.node.writeValue(
389
- identifierType,
390
- identifier,
391
- isByteString
392
- ? Buffer.from(payload)
393
- : payload
394
- );
450
+ try {
451
+ this.node.writeValue(
452
+ identifierType,
453
+ identifier,
454
+ isByteString
455
+ ? Buffer.from(payload)
456
+ : payload
457
+ );
458
+ } catch (e) {
459
+ directError = { identifier, message: e.message || String(e) };
460
+ msg.payload = null;
461
+ }
395
462
 
396
463
  writtenPaths = [identifier];
397
464
  }
@@ -403,13 +470,13 @@ class OpcUaServerProcess {
403
470
  throw new Error("msg.payload array does not contain any items");
404
471
  }
405
472
 
406
- payload.forEach(item => {
407
- this.writePayloadItem(identifierType, item);
408
- });
409
-
410
- writtenPaths = payload.map(item =>
411
- this.resolvePayloadItemIdentifier(item)
473
+ const writeArrayResults = payload.map(item =>
474
+ this.writePayloadItem(identifierType, item)
412
475
  );
476
+
477
+ msg.payload = writeArrayResults;
478
+
479
+ writtenPaths = writeArrayResults.map(item => item.path);
413
480
  }
414
481
 
415
482
  // Objeto { path: value }
@@ -429,11 +496,15 @@ class OpcUaServerProcess {
429
496
  }
430
497
 
431
498
  identifiers.forEach(identifier => {
432
- this.node.writeValue(
433
- identifierType,
434
- identifier,
435
- payload[identifier]
436
- );
499
+ try {
500
+ this.node.writeValue(
501
+ identifierType,
502
+ identifier,
503
+ payload[identifier]
504
+ );
505
+ } catch (e) {
506
+ // suppress per-item errors; unknown paths become undefined
507
+ }
437
508
  });
438
509
 
439
510
  writtenPaths = identifiers;
@@ -443,12 +514,16 @@ class OpcUaServerProcess {
443
514
  else {
444
515
 
445
516
  const identifier = this.resolveIdentifier(target);
446
-
447
- this.node.writeValue(
448
- identifierType,
449
- identifier,
450
- payload
451
- );
517
+ try {
518
+ this.node.writeValue(
519
+ identifierType,
520
+ identifier,
521
+ payload
522
+ );
523
+ } catch (e) {
524
+ directError = { identifier, message: e.message || String(e) };
525
+ msg.payload = null;
526
+ }
452
527
 
453
528
  writtenPaths = [identifier];
454
529
  }
@@ -466,25 +541,66 @@ class OpcUaServerProcess {
466
541
  msg.topic = writtenPaths[0];
467
542
  }
468
543
 
469
- process.send({
470
- type: "send",
471
- data: msg,
472
- nodeId
473
- });
544
+ let hasFailures = false;
545
+ if (Array.isArray(msg.payload) && msg.payload.length && msg.payload[0] && typeof msg.payload[0].status === "string") {
546
+ const failed = msg.payload.filter(item => item.status !== "Good");
547
+ if (failed.length) {
548
+ hasFailures = true;
549
+ }
550
+ }
551
+ if (directError) {
552
+ hasFailures = true;
553
+ }
554
+
555
+ if (!hasFailures) {
556
+ process.send({
557
+ type: "send",
558
+ data: msg,
559
+ nodeId
560
+ });
561
+ }
474
562
 
475
563
  process.send({
476
564
  type: "status",
477
565
  data: {
478
- fill: "green",
566
+ fill: hasFailures ? (directError ? "red" : "yellow") : "green",
479
567
  shape: "dot",
480
- text:
481
- writtenPaths.length > 1
482
- ? `write ${writtenPaths.length} tags`
483
- : `write ${writtenPaths[0]}`
568
+ text: hasFailures
569
+ ? (directError ? "write failed" : "partial write failed")
570
+ : (writtenPaths.length > 1 ? `write ${writtenPaths.length} tags` : `write ${writtenPaths[0]}`)
484
571
  },
485
572
  nodeId
486
573
  });
487
574
 
575
+ // Emit a partialError for items that could not be written (array-of-objects mode only)
576
+ if (Array.isArray(msg.payload) && msg.payload.length && msg.payload[0] && typeof msg.payload[0].status === "string") {
577
+ const failed = msg.payload
578
+ .filter(item => item.status !== "Good")
579
+ .map(item => ({ name: item.name, path: item.path, status: item.status }));
580
+
581
+ if (failed.length) {
582
+ process.send({
583
+ type: "partialError",
584
+ error: "Some tags could not be written: " + failed.map(f => f.path).join(", "),
585
+ failed: failed,
586
+ originalMsg: msg,
587
+ nodeId
588
+ });
589
+ }
590
+ }
591
+
592
+ // Emit a partialError for a direct single-tag write failure
593
+ if (directError) {
594
+ const { identifier, message } = directError;
595
+ process.send({
596
+ type: "partialError",
597
+ error: "Some tags could not be written: " + identifier,
598
+ failed: [{ name: "", path: identifier, status: message }],
599
+ originalMsg: msg,
600
+ nodeId
601
+ });
602
+ }
603
+
488
604
  } catch (error) {
489
605
 
490
606
  process.send({
@@ -495,6 +611,7 @@ class OpcUaServerProcess {
495
611
  text: "failed write"
496
612
  },
497
613
  error: error.message,
614
+ originalMsg: msg,
498
615
  nodeId
499
616
  });
500
617
  }
@@ -541,20 +658,38 @@ class OpcUaServerProcess {
541
658
 
542
659
  readPayloadItem(identifierType, item) {
543
660
  const identifier = this.resolvePayloadItemIdentifier(item);
661
+ let value = null;
662
+ let status = "Good";
663
+ try {
664
+ value = this.node.readValue(identifierType, identifier);
665
+ } catch (e) {
666
+ value = null;
667
+ status = e.message || String(e);
668
+ }
544
669
  return {
545
670
  name: item.name,
546
671
  path: identifier,
547
- value: this.node.readValue(identifierType, identifier)
672
+ value,
673
+ status
548
674
  };
549
675
  }
550
676
 
551
677
  writePayloadItem(identifierType, item) {
552
678
  const identifier = this.resolvePayloadItemIdentifier(item);
553
- this.node.writeValue(identifierType, identifier, item.value);
679
+ let writtenValue = null;
680
+ let status = "Good";
681
+ try {
682
+ this.node.writeValue(identifierType, identifier, item.value);
683
+ writtenValue = item.value;
684
+ } catch (e) {
685
+ writtenValue = null;
686
+ status = e.message || String(e);
687
+ }
554
688
  return {
555
689
  name: item.name,
556
690
  path: identifier,
557
- value: item.value
691
+ value: writtenValue,
692
+ status
558
693
  };
559
694
  }
560
695
 
@@ -775,6 +910,222 @@ class OpcUaServerProcess {
775
910
  }
776
911
  }
777
912
 
913
+ async readActiveSessions(msg, nodeId) {
914
+ try {
915
+ await this.ensureReady();
916
+ const server = this.node && this.node.server;
917
+ if (!server || !server.engine) {
918
+ throw new Error("OPC UA server is not available");
919
+ }
920
+
921
+ const rawSessions = server.engine._sessions || {};
922
+ const sessions = Object.values(rawSessions).map((session) => buildSessionSnapshot(session));
923
+
924
+ if (msg) {
925
+ const outMsg = Object.assign({}, msg);
926
+ outMsg.payload = sessions;
927
+
928
+ process.send({
929
+ type: "send",
930
+ data: outMsg,
931
+ nodeId: nodeId
932
+ });
933
+ }
934
+
935
+ process.send({
936
+ type: "status",
937
+ data: {
938
+ fill: "green",
939
+ shape: "dot",
940
+ text: sessions.length === 1
941
+ ? "1 session"
942
+ : sessions.length + " sessions"
943
+ },
944
+ nodeId: nodeId
945
+ });
946
+ } catch (error) {
947
+ if (msg) {
948
+ process.send({
949
+ type: "error",
950
+ data: { fill: "red", shape: "ring", text: "failed getSessions" },
951
+ error: error.message,
952
+ originalMsg: msg,
953
+ nodeId: nodeId
954
+ });
955
+ } else {
956
+ process.send({
957
+ type: "status",
958
+ data: { fill: "red", shape: "ring", text: "failed getSessions: " + error.message },
959
+ nodeId: nodeId
960
+ });
961
+ }
962
+ }
963
+ }
964
+
965
+ async deleteActiveSessions(msg, nodeId) {
966
+ try {
967
+ await this.ensureReady();
968
+ const server = this.node && this.node.server;
969
+ if (!server || !server.engine) {
970
+ throw new Error("OPC UA server is not available");
971
+ }
972
+
973
+ const payload = msg && Array.isArray(msg.payload) ? msg.payload : [];
974
+ if (!payload.length) {
975
+ throw new Error("msg.payload must be a non-empty array of { sessionId } objects");
976
+ }
977
+
978
+ const engine = server.engine;
979
+ const rawSessions = engine._sessions || {};
980
+
981
+ const results = payload.map((item) => {
982
+ const requestedId = String(item && item.sessionId || "").trim();
983
+ if (!requestedId) {
984
+ return { sessionId: requestedId, status: "error", error: "sessionId is required" };
985
+ }
986
+
987
+ // Sessions are keyed by authenticationToken; find by matching nodeId (the GUID sessionId)
988
+ const found = Object.values(rawSessions).find(
989
+ (s) => safeToString(s.nodeId) === requestedId
990
+ );
991
+
992
+ if (!found) {
993
+ return { sessionId: requestedId, status: "not_found" };
994
+ }
995
+
996
+ try {
997
+ engine.closeSession(found.authenticationToken, true, "Forcing");
998
+ return { sessionId: requestedId, status: "deleted" };
999
+ } catch (closeError) {
1000
+ return { sessionId: requestedId, status: "error", error: closeError.message };
1001
+ }
1002
+ });
1003
+
1004
+ const deletedCount = results.filter((r) => r.status === "deleted").length;
1005
+ const notFoundCount = results.filter((r) => r.status === "not_found").length;
1006
+ const errorCount = results.filter((r) => r.status === "error").length;
1007
+
1008
+ if (msg) {
1009
+ const outMsg = Object.assign({}, msg);
1010
+ outMsg.payload = results;
1011
+
1012
+ process.send({
1013
+ type: "send",
1014
+ data: outMsg,
1015
+ nodeId: nodeId
1016
+ });
1017
+ }
1018
+
1019
+ const statusParts = [];
1020
+ if (deletedCount) statusParts.push("deleted " + deletedCount);
1021
+ if (notFoundCount) statusParts.push("not found " + notFoundCount);
1022
+ if (errorCount) statusParts.push("error " + errorCount);
1023
+
1024
+ process.send({
1025
+ type: "status",
1026
+ data: {
1027
+ fill: errorCount ? "red" : (notFoundCount ? "yellow" : "green"),
1028
+ shape: "dot",
1029
+ text: statusParts.join(", ") || "no sessions"
1030
+ },
1031
+ nodeId: nodeId
1032
+ });
1033
+ } catch (error) {
1034
+ if (msg) {
1035
+ process.send({
1036
+ type: "error",
1037
+ data: { fill: "red", shape: "ring", text: "failed deleteSessions" },
1038
+ error: error.message,
1039
+ originalMsg: msg,
1040
+ nodeId: nodeId
1041
+ });
1042
+ } else {
1043
+ process.send({
1044
+ type: "status",
1045
+ data: { fill: "red", shape: "ring", text: "failed deleteSessions: " + error.message },
1046
+ nodeId: nodeId
1047
+ });
1048
+ }
1049
+ }
1050
+ }
1051
+
1052
+ }
1053
+
1054
+ /**
1055
+ * Serializes a node-opcua ServerSession into a plain, IPC-safe object.
1056
+ */
1057
+ function buildSessionSnapshot(session) {
1058
+ return {
1059
+ sessionId: safeToString(session.nodeId),
1060
+ sessionName: String(session.sessionName || ""),
1061
+ status: String(session.__status || ""),
1062
+ creationDate: session.creationDate instanceof Date ? session.creationDate.toISOString() : null,
1063
+ sessionTimeout: safeNumber(session.sessionTimeout),
1064
+ clientLastContactTime: safeNumber(session.clientLastContactTime),
1065
+ channelId: session.channelId != null ? session.channelId : null,
1066
+ clientDescription: buildClientDescription(session.clientDescription),
1067
+ userIdentityToken: buildUserIdentityToken(session.userIdentityToken),
1068
+ channel: buildChannelInfo(session.channel),
1069
+ currentSubscriptionCount: safeNumber(session.currentSubscriptionCount),
1070
+ cumulatedSubscriptionCount: safeNumber(session.cumulatedSubscriptionCount),
1071
+ currentMonitoredItemCount: safeNumber(session.currentMonitoredItemCount),
1072
+ aborted: Boolean(session.aborted)
1073
+ };
1074
+ }
1075
+
1076
+ function safeToString(value) {
1077
+ try {
1078
+ return value != null ? String(value) : null;
1079
+ } catch (_) {
1080
+ return null;
1081
+ }
1082
+ }
1083
+
1084
+ function safeNumber(value) {
1085
+ const n = Number(value);
1086
+ return Number.isFinite(n) ? n : null;
1087
+ }
1088
+
1089
+ function buildClientDescription(desc) {
1090
+ if (!desc || typeof desc !== "object") {
1091
+ return null;
1092
+ }
1093
+ return {
1094
+ applicationUri: safeToString(desc.applicationUri),
1095
+ productUri: safeToString(desc.productUri),
1096
+ applicationName: desc.applicationName && desc.applicationName.text
1097
+ ? String(desc.applicationName.text)
1098
+ : safeToString(desc.applicationName),
1099
+ applicationType: safeToString(desc.applicationType)
1100
+ };
1101
+ }
1102
+
1103
+ function buildUserIdentityToken(token) {
1104
+ if (!token || typeof token !== "object") {
1105
+ return null;
1106
+ }
1107
+ return {
1108
+ policyId: safeToString(token.policyId),
1109
+ userName: safeToString(token.userName),
1110
+ // Never expose passwords or raw credential bytes
1111
+ tokenType: safeToString(token.schema && token.schema.name)
1112
+ };
1113
+ }
1114
+
1115
+ function buildChannelInfo(channel) {
1116
+ if (!channel || typeof channel !== "object") {
1117
+ return null;
1118
+ }
1119
+ return {
1120
+ channelId: channel.channelId != null ? channel.channelId : null,
1121
+ remoteAddress: safeToString(channel.remoteAddress),
1122
+ remotePort: safeNumber(channel.remotePort),
1123
+ bytesRead: safeNumber(channel.bytesRead),
1124
+ bytesWritten: safeNumber(channel.bytesWritten),
1125
+ transactionsCount: safeNumber(channel.transactionsCount),
1126
+ securityMode: safeToString(channel.securityMode),
1127
+ securityPolicy: safeToString(channel.securityPolicy)
1128
+ };
778
1129
  }
779
1130
 
780
1131
  /**
@@ -827,14 +1178,33 @@ process.on("message", async (msg) => {
827
1178
  break;
828
1179
 
829
1180
  case "buildServerSnapshot":
830
- OpcUaServerStatusNode(serverProcess.node, msg.msg, msg.nodeId)
831
- break
1181
+ try {
1182
+ await serverProcess.ensureReady();
1183
+ OpcUaServerStatusNode(serverProcess.node, msg.msg, msg.nodeId);
1184
+ } catch (error) {
1185
+ process.send({
1186
+ type: "status",
1187
+ data: {
1188
+ fill: msg.msg ? "red" : "yellow",
1189
+ shape: "ring",
1190
+ text: msg.msg ? "Status: " + error.message : "waiting for server"
1191
+ },
1192
+ nodeId: msg.nodeId
1193
+ });
1194
+ }
1195
+ break;
832
1196
  case "eventsServer":
833
1197
  eventsServer(serverProcess.node, msg.node, msg.nodeId)
834
1198
  break;
835
1199
  case "readActiveAlarms":
836
1200
  serverProcess.readActiveAlarms(msg.msg, msg.nodeId)
837
1201
  break;
1202
+ case "readActiveSessions":
1203
+ await serverProcess.readActiveSessions(msg.msg, msg.nodeId);
1204
+ break;
1205
+ case "deleteActiveSessions":
1206
+ await serverProcess.deleteActiveSessions(msg.msg, msg.nodeId);
1207
+ break;
838
1208
 
839
1209
 
840
1210