@vitormnm/node-red-simple-opcua 1.6.3 → 1.8.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.
- package/README.md +104 -136
- package/client/lib/opcua-client-browser.js +254 -11
- package/client/lib/opcua-client-method-service.js +1 -1
- package/client/lib/opcua-client-subscription-service.js +0 -2
- package/client/lib/opcua-client-write-service.js +14 -4
- package/client/opcua-client-config.html +118 -1
- package/client/opcua-client-config.js +112 -9
- package/client/opcua-client-help.html +6 -0
- package/client/opcua-client-utils.js +158 -10
- package/client/opcua-client.html +8 -0
- package/client/opcua-client.js +97 -1
- package/client/view/opcua-client.js +106 -14
- package/examples/flows_simple_opc.json +1 -1
- package/package.json +2 -2
- package/server/lib/opcua-address-space-alarm.js +95 -32
- package/server/lib/opcua-address-space-builder.js +717 -59
- package/server/lib/opcua-config.js +110 -35
- package/server/lib/opcua-server-events-child.js +31 -5
- package/server/lib/opcua-server-runtime-child.js +424 -27
- package/server/lib/opcua-server-runtime.js +52 -5
- package/server/lib/opcua-server-status-child.js +46 -15
- package/server/nodered/simple_opcua/server/certificates/mutex +0 -0
- package/server/nodered/simple_opcua/server/certificates/own/certs/server_selfsigned_cert_2048.pem +25 -0
- package/server/nodered/simple_opcua/server/certificates/own/certs/server_selfsigned_cert_2048.pem.mutex +0 -0
- package/server/nodered/simple_opcua/server/certificates/own/openssl.cnf +72 -0
- package/server/nodered/simple_opcua/server/certificates/own/private/private_key.pem +28 -0
- package/server/nodered/simple_opcua/server/certificates/trusted/certs/NodeOPCUA-Client@tuf[c5a9e20a8b680cdff76aaf0165bb3c9318da37a5].pem +25 -0
- package/server/nodered/simple_opcua/server/myServer1/mutex +0 -0
- package/server/nodered/simple_opcua/server/myServer1/own/certs/server_selfsigned_cert_2048.pem +25 -0
- package/server/nodered/simple_opcua/server/myServer1/own/certs/server_selfsigned_cert_2048.pem.mutex +0 -0
- package/server/nodered/simple_opcua/server/myServer1/own/openssl.cnf +72 -0
- package/server/nodered/simple_opcua/server/myServer1/own/private/private_key.pem +28 -0
- package/server/nodered/simple_opcua/server/myServer1/trusted/certs/NodeOPCUA-Client@tuf[91e520c64ff891c67168f08a46dd194071e15dae].pem +25 -0
- package/server/nodered/simple_opcua/server/myServer1/trusted/certs/NodeOPCUA-Client@tuf[98ae95da627cea4c500753c319161a3554ee38d7].pem +25 -0
- package/server/nodered/simple_opcua/server/myServer1/trusted/certs/NodeOPCUA-Client@tuf[aef8d7a1cfba13d84189a0bcf1694208fc51a7f9].pem +25 -0
- package/server/nodered/simple_opcua/server/myServer1/trusted/certs/NodeOPCUA-Client@tuf[c5a9e20a8b680cdff76aaf0165bb3c9318da37a5].pem +25 -0
- package/server/nodered/simple_opcua/server/myServer1/trusted/certs/NodeOPCUA-Client@tuf[ebdf9acf1d02e347917a14108d3144799c638ea3].pem +25 -0
- package/server/opcua-server-io.html +93 -1
- package/server/opcua-server-io.js +153 -29
- package/server/opcua-server-registry.js +8 -2
- package/server/opcua-server.css +64 -0
- package/server/opcua-server.html +168 -44
- package/server/opcua-server.js +115 -5
- package/server/view/opcua-server.css +100 -6
- package/server/view/opcua-server.js +746 -48
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
var editorState = { objects: [], folders: [], objectsTypes: [], enumerations: [], nameSpaces: [] };
|
|
4
4
|
var expansionState = {};
|
|
5
5
|
var selectedPath = "";
|
|
6
|
+
var draggedPath = null;
|
|
6
7
|
var pendingCreate = null;
|
|
7
8
|
var pendingPasswordHashes = 0;
|
|
8
9
|
var authGroups = [];
|
|
@@ -12,13 +13,136 @@
|
|
|
12
13
|
var isSyncing = false;
|
|
13
14
|
var DEFAULT_NAMESPACE_ID = 2;
|
|
14
15
|
|
|
16
|
+
var selectedCertFolder = "rejected";
|
|
17
|
+
var selectedCertName = "";
|
|
18
|
+
var certificatesData = { trusted: [], rejected: [] };
|
|
19
|
+
|
|
15
20
|
function syncModalBodyClass() {
|
|
16
|
-
$("body").toggleClass("opcua-tree-modal-open",
|
|
21
|
+
$("body").toggleClass("opcua-tree-modal-open",
|
|
22
|
+
$("#node-input-tree-modal").is(":visible") ||
|
|
23
|
+
$("#node-input-auth-modal").is(":visible") ||
|
|
24
|
+
$("#node-input-cert-modal").is(":visible") ||
|
|
25
|
+
$("#node-input-settings-modal").is(":visible")
|
|
26
|
+
);
|
|
17
27
|
}
|
|
18
28
|
function openTreeModal() { $("#node-input-tree-modal").show(); syncModalBodyClass(); }
|
|
19
29
|
function closeTreeModal() { $("#node-input-tree-modal").hide(); syncModalBodyClass(); }
|
|
20
30
|
function openAuthModal() { $("#node-input-auth-modal").show(); syncModalBodyClass(); renderAuthEditor(); }
|
|
21
31
|
function closeAuthModal() { $("#node-input-auth-modal").hide(); syncModalBodyClass(); }
|
|
32
|
+
function openCertModal() { $("#node-input-cert-modal").show(); syncModalBodyClass(); initCertEditor(); }
|
|
33
|
+
function closeCertModal() { $("#node-input-cert-modal").hide(); syncModalBodyClass(); }
|
|
34
|
+
function openSettingsModal() { $("#node-input-settings-modal").show(); syncModalBodyClass(); }
|
|
35
|
+
function closeSettingsModal() { $("#node-input-settings-modal").hide(); syncModalBodyClass(); }
|
|
36
|
+
|
|
37
|
+
function initCertEditor() {
|
|
38
|
+
selectedCertFolder = "rejected";
|
|
39
|
+
selectedCertName = "";
|
|
40
|
+
$("#opcua-cert-details").hide();
|
|
41
|
+
$("#opcua-cert-folders .opcua-cert-item").removeClass("is-selected");
|
|
42
|
+
$('#opcua-cert-folders .opcua-cert-item[data-folder="rejected"]').addClass("is-selected");
|
|
43
|
+
fetchCertificates();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function fetchCertificates() {
|
|
47
|
+
var filesContainer = $("#opcua-cert-files");
|
|
48
|
+
filesContainer.empty().append('<div class="opcua-tree-empty"><i class="fa fa-spinner fa-spin"></i> Loading certificates...</div>');
|
|
49
|
+
|
|
50
|
+
var currentServerName = $("#node-input-serverName").val() || "";
|
|
51
|
+
|
|
52
|
+
$.ajax({
|
|
53
|
+
url: "opc-ua-server/certificates",
|
|
54
|
+
type: "GET",
|
|
55
|
+
data: {
|
|
56
|
+
serverName: currentServerName
|
|
57
|
+
},
|
|
58
|
+
dataType: "json",
|
|
59
|
+
success: function (data) {
|
|
60
|
+
certificatesData = data || { trusted: [], rejected: [] };
|
|
61
|
+
renderCertificatesList();
|
|
62
|
+
},
|
|
63
|
+
error: function (xhr, textStatus, errorThrown) {
|
|
64
|
+
filesContainer.empty().append('<div class="opcua-tree-empty" style="color: #d9534f;"><i class="fa fa-exclamation-triangle"></i> Failed to load certificates.</div>');
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function renderCertificatesList() {
|
|
70
|
+
var filesContainer = $("#opcua-cert-files");
|
|
71
|
+
filesContainer.empty();
|
|
72
|
+
|
|
73
|
+
var list = certificatesData[selectedCertFolder] || [];
|
|
74
|
+
if (list.length === 0) {
|
|
75
|
+
filesContainer.append('<div class="opcua-tree-empty">No certificates found in this folder.</div>');
|
|
76
|
+
$("#opcua-cert-details").hide();
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
list.forEach(function (filename) {
|
|
81
|
+
var item = $('<div class="opcua-cert-item"></div>');
|
|
82
|
+
item.attr("data-name", filename);
|
|
83
|
+
item.append('<i class="fa fa-certificate"></i> ' + escapeHtml(filename));
|
|
84
|
+
if (filename === selectedCertName) {
|
|
85
|
+
item.addClass("is-selected");
|
|
86
|
+
}
|
|
87
|
+
filesContainer.append(item);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// If previously selected cert is not in the list, hide details
|
|
91
|
+
if (selectedCertName && list.indexOf(selectedCertName) === -1) {
|
|
92
|
+
selectedCertName = "";
|
|
93
|
+
$("#opcua-cert-details").hide();
|
|
94
|
+
} else if (selectedCertName) {
|
|
95
|
+
showCertificateDetails();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function showCertificateDetails() {
|
|
100
|
+
$("#opcua-selected-cert-name").text(selectedCertName);
|
|
101
|
+
var targetSelect = $("#opcua-cert-target-folder");
|
|
102
|
+
targetSelect.empty();
|
|
103
|
+
|
|
104
|
+
if (selectedCertFolder === "rejected") {
|
|
105
|
+
targetSelect.append('<option value="trusted">Trusted Certificates</option>');
|
|
106
|
+
} else {
|
|
107
|
+
targetSelect.append('<option value="rejected">Rejected Certificates</option>');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
$("#opcua-cert-details").show();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function moveCertificate() {
|
|
114
|
+
if (!selectedCertName) return;
|
|
115
|
+
var targetFolder = $("#opcua-cert-target-folder").val();
|
|
116
|
+
if (!targetFolder) return;
|
|
117
|
+
|
|
118
|
+
var currentServerName = $("#node-input-serverName").val() || "";
|
|
119
|
+
var moveBtn = $("#opcua-cert-move-btn");
|
|
120
|
+
moveBtn.addClass("disabled").append(' <i class="fa fa-spinner fa-spin"></i>');
|
|
121
|
+
|
|
122
|
+
$.ajax({
|
|
123
|
+
url: "opc-ua-server/certificates/move",
|
|
124
|
+
type: "POST",
|
|
125
|
+
contentType: "application/json",
|
|
126
|
+
data: JSON.stringify({
|
|
127
|
+
serverName: currentServerName,
|
|
128
|
+
filename: selectedCertName,
|
|
129
|
+
fromFolder: selectedCertFolder,
|
|
130
|
+
toFolder: targetFolder
|
|
131
|
+
}),
|
|
132
|
+
success: function (res) {
|
|
133
|
+
moveBtn.removeClass("disabled").find("i.fa-spin").remove();
|
|
134
|
+
selectedCertName = "";
|
|
135
|
+
$("#opcua-cert-details").hide();
|
|
136
|
+
RED.notify("Certificate moved successfully.", "success");
|
|
137
|
+
fetchCertificates();
|
|
138
|
+
},
|
|
139
|
+
error: function (xhr, textStatus, errorThrown) {
|
|
140
|
+
moveBtn.removeClass("disabled").find("i.fa-spin").remove();
|
|
141
|
+
var errMsg = xhr.responseJSON && xhr.responseJSON.error ? xhr.responseJSON.error : "Failed to move certificate.";
|
|
142
|
+
RED.notify(errMsg, "error");
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
}
|
|
22
146
|
|
|
23
147
|
function parseTree(rawValue, strict) {
|
|
24
148
|
if (!rawValue) return { objects: [], folders: [], objectsTypes: [], enumerations: [], nameSpaces: [] };
|
|
@@ -174,19 +298,20 @@
|
|
|
174
298
|
variableNodeId: alarm.variableNodeId ? String(alarm.variableNodeId) : "",
|
|
175
299
|
type: type,
|
|
176
300
|
enabled: alarm.enabled !== undefined ? !!alarm.enabled : true,
|
|
301
|
+
sendValue: alarm.sendValue !== undefined ? !!alarm.sendValue : true,
|
|
177
302
|
severity: alarm.severity !== undefined ? alarm.severity : 500,
|
|
178
303
|
description: alarm.description ? String(alarm.description) : "",
|
|
179
304
|
displayName: alarm.displayName ? String(alarm.displayName) : "",
|
|
180
305
|
nodeId: alarm.nodeId ? String(alarm.nodeId) : "",
|
|
181
306
|
namespaceId: normalizeNamespaceId(alarm.namespaceId),
|
|
182
307
|
accessPermission: normalizeAccessPermissionValues(alarm.accessPermission || alarm.accessPermissions),
|
|
183
|
-
highHighLimit: alarm.highHighLimit !== undefined ? alarm.highHighLimit :
|
|
308
|
+
highHighLimit: alarm.highHighLimit !== undefined ? alarm.highHighLimit : 90,
|
|
184
309
|
highHighMessage: alarm.highHighMessage ? String(alarm.highHighMessage) : "High High alarm",
|
|
185
310
|
highLimit: alarm.highLimit !== undefined ? alarm.highLimit : 80,
|
|
186
311
|
highMessage: alarm.highMessage ? String(alarm.highMessage) : "High alarm",
|
|
187
312
|
lowLimit: alarm.lowLimit !== undefined ? alarm.lowLimit : 20,
|
|
188
313
|
lowMessage: alarm.lowMessage ? String(alarm.lowMessage) : "Low alarm",
|
|
189
|
-
lowLowLimit: alarm.lowLowLimit !== undefined ? alarm.lowLowLimit :
|
|
314
|
+
lowLowLimit: alarm.lowLowLimit !== undefined ? alarm.lowLowLimit : 10,
|
|
190
315
|
lowLowMessage: alarm.lowLowMessage ? String(alarm.lowLowMessage) : "Low Low alarm",
|
|
191
316
|
normalStateValue: alarm.normalStateValue !== undefined ? alarm.normalStateValue : 0,
|
|
192
317
|
digitalMessage: alarm.digitalMessage ? String(alarm.digitalMessage) : "Digital alarm"
|
|
@@ -319,7 +444,73 @@
|
|
|
319
444
|
|
|
320
445
|
function normalizeSearchTerm(value) { return String(value || "").trim().toLowerCase(); }
|
|
321
446
|
function isExpanded(path, defaultValue) { if (expansionState[path] === undefined) expansionState[path] = !!defaultValue; return expansionState[path]; }
|
|
447
|
+
function isAncestorPath(ancestorPath, descendantPath) {
|
|
448
|
+
if (ancestorPath === descendantPath) return true;
|
|
449
|
+
return descendantPath.indexOf(ancestorPath + ".") === 0;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
function isValidDropTarget(srcPath, destPath) {
|
|
453
|
+
if (srcPath === destPath) return false;
|
|
454
|
+
if (isAncestorPath(srcPath, destPath)) return false;
|
|
455
|
+
if (srcPath.indexOf("virtual:") === 0 || srcPath.indexOf("nameSpaces.") === 0) return false;
|
|
456
|
+
|
|
457
|
+
var srcClass = nodeClassFromPath(srcPath);
|
|
458
|
+
|
|
459
|
+
if (destPath === "virtual:Objects") {
|
|
460
|
+
return srcClass === "Folder" || srcClass === "Object";
|
|
461
|
+
}
|
|
462
|
+
if (destPath === "virtual:Types.ObjectTypes") {
|
|
463
|
+
var srcTokens = pathToTokens(srcPath);
|
|
464
|
+
return srcClass === "ObjectType" && srcTokens.length === 2 && srcTokens[0] === "objectsTypes";
|
|
465
|
+
}
|
|
466
|
+
if (destPath === "virtual:Types.DataTypes") {
|
|
467
|
+
var srcTokens = pathToTokens(srcPath);
|
|
468
|
+
return srcClass === "Enumeration" && srcTokens.length === 2 && srcTokens[0] === "enumerations";
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
if (destPath.indexOf("virtual:") === 0 || destPath.indexOf("nameSpaces.") === 0) {
|
|
472
|
+
return false;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// Both are real paths. Make sure they are in the same scope (Objects vs Types/ObjectTypes)
|
|
476
|
+
var srcIsTemplate = isObjectTypeModelPath(srcPath);
|
|
477
|
+
var destIsTemplate = isObjectTypeModelPath(destPath);
|
|
478
|
+
if (srcIsTemplate !== destIsTemplate) {
|
|
479
|
+
return false;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
var destClass = nodeClassFromPath(destPath);
|
|
483
|
+
if (destClass === "Folder" || destClass === "Object" || destClass === "ObjectType") {
|
|
484
|
+
return srcClass !== "Enumeration" && srcClass !== "Namespace";
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
return false;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
function findObjectPath(obj, targetObj, currentPath) {
|
|
491
|
+
if (obj === targetObj) return currentPath;
|
|
492
|
+
if (typeof obj !== "object" || obj === null) return null;
|
|
493
|
+
|
|
494
|
+
if (Array.isArray(obj)) {
|
|
495
|
+
for (var i = 0; i < obj.length; i++) {
|
|
496
|
+
var path = findObjectPath(obj[i], targetObj, currentPath ? currentPath + "." + i : String(i));
|
|
497
|
+
if (path) return path;
|
|
498
|
+
}
|
|
499
|
+
} else {
|
|
500
|
+
var keys = Object.keys(obj);
|
|
501
|
+
for (var i = 0; i < keys.length; i++) {
|
|
502
|
+
var k = keys[i];
|
|
503
|
+
if (k === "folders" || k === "objects" || k === "variables" || k === "methods" || k === "alarms" || k === "objectsTypes" || k === "enumerations") {
|
|
504
|
+
var path = findObjectPath(obj[k], targetObj, currentPath ? currentPath + "." + k : k);
|
|
505
|
+
if (path) return path;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
return null;
|
|
510
|
+
}
|
|
511
|
+
|
|
322
512
|
function nodeClassFromPath(path) {
|
|
513
|
+
if (path && path.indexOf("virtual:") === 0) return "VisualFolder";
|
|
323
514
|
var tokens = pathToTokens(path);
|
|
324
515
|
if (!tokens.length) return "Object";
|
|
325
516
|
|
|
@@ -333,7 +524,18 @@
|
|
|
333
524
|
if (collectionToken === "folders") return "Folder";
|
|
334
525
|
return "Object";
|
|
335
526
|
}
|
|
336
|
-
function
|
|
527
|
+
function getVirtualNodeName(path) {
|
|
528
|
+
if (path === "virtual:Objects") return "Objects";
|
|
529
|
+
if (path === "virtual:Types") return "Types";
|
|
530
|
+
if (path === "virtual:Types.ObjectTypes") return "ObjectTypes";
|
|
531
|
+
if (path === "virtual:Types.DataTypes") return "DataTypes";
|
|
532
|
+
return "";
|
|
533
|
+
}
|
|
534
|
+
function getNodeDisplayName(path) {
|
|
535
|
+
if (path && path.indexOf("virtual:") === 0) return getVirtualNodeName(path);
|
|
536
|
+
var item = getAtPath(editorState, path);
|
|
537
|
+
return item ? (item.name || "(unnamed)") : "";
|
|
538
|
+
}
|
|
337
539
|
function getNamespaceOptions() {
|
|
338
540
|
return Array.isArray(editorState.nameSpaces) ? editorState.nameSpaces.slice().sort(function (left, right) { return left.id - right.id; }) : [];
|
|
339
541
|
}
|
|
@@ -444,7 +646,7 @@
|
|
|
444
646
|
function nodeIdSuffixFromValue(nodeId, defaultSuffix) {
|
|
445
647
|
var raw = String(nodeId || "").trim();
|
|
446
648
|
if (!raw) return defaultSuffix;
|
|
447
|
-
var match = /^ns=\d+;
|
|
649
|
+
var match = /^ns=\d+;[si]=(.*)$/.exec(raw);
|
|
448
650
|
if (match) return match[1];
|
|
449
651
|
return raw;
|
|
450
652
|
}
|
|
@@ -485,8 +687,69 @@
|
|
|
485
687
|
var item = getAtPath(editorState, path);
|
|
486
688
|
if (isObjectTypeModelPath(path)) return buildGeneratedNodeIdForPath(path);
|
|
487
689
|
var customNodeId = item && item.nodeId ? String(item.nodeId).trim() : "";
|
|
488
|
-
|
|
489
|
-
|
|
690
|
+
if (customNodeId) {
|
|
691
|
+
if (/^ns=\d+;[si]=/.test(customNodeId)) {
|
|
692
|
+
return customNodeId;
|
|
693
|
+
}
|
|
694
|
+
return getNodeIdPrefix(getNodeNamespaceId(path)) + customNodeId;
|
|
695
|
+
}
|
|
696
|
+
return buildGeneratedNodeIdForPath(path);
|
|
697
|
+
}
|
|
698
|
+
function parseNodeId(nodeId, defaultSuffix) {
|
|
699
|
+
var raw = String(nodeId || "").trim();
|
|
700
|
+
if (!raw) {
|
|
701
|
+
return { type: "s", value: defaultSuffix };
|
|
702
|
+
}
|
|
703
|
+
var match = /^ns=\d+;([si])=(.*)$/.exec(raw);
|
|
704
|
+
if (match) {
|
|
705
|
+
return { type: match[1], value: match[2] };
|
|
706
|
+
}
|
|
707
|
+
return { type: "s", value: raw };
|
|
708
|
+
}
|
|
709
|
+
function updateNodeIdValueInputState(mode, type) {
|
|
710
|
+
var inputId = "#opcua-" + mode + "-nodeid-value";
|
|
711
|
+
var labelId = "#opcua-" + mode + "-nodeid-value-label";
|
|
712
|
+
var input = $(inputId);
|
|
713
|
+
var label = $(labelId);
|
|
714
|
+
|
|
715
|
+
if (type === "i") {
|
|
716
|
+
if (label.length) label.text("nodeId Value (Numeric)");
|
|
717
|
+
input.attr("type", "number");
|
|
718
|
+
input.attr("step", "1");
|
|
719
|
+
input.attr("placeholder", "Enter a number");
|
|
720
|
+
} else {
|
|
721
|
+
if (label.length) label.text("nodeId Value (String)");
|
|
722
|
+
input.attr("type", "text");
|
|
723
|
+
input.removeAttr("step");
|
|
724
|
+
if (mode === "create") {
|
|
725
|
+
input.attr("placeholder", "Leave blank for default (s)");
|
|
726
|
+
} else {
|
|
727
|
+
input.attr("placeholder", "Enter a string");
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
function saveDetailNodeId(path) {
|
|
732
|
+
if (!path) return;
|
|
733
|
+
var type = $("#opcua-detail-nodeid-type").val();
|
|
734
|
+
var rawVal = $("#opcua-detail-nodeid-value").val();
|
|
735
|
+
var nsId = getNodeNamespaceId(path);
|
|
736
|
+
|
|
737
|
+
var customNodeId = "";
|
|
738
|
+
if (rawVal) {
|
|
739
|
+
if (type === "i") {
|
|
740
|
+
var numVal = parseInt(rawVal, 10);
|
|
741
|
+
if (!isNaN(numVal)) {
|
|
742
|
+
customNodeId = "ns=" + nsId + ";i=" + numVal;
|
|
743
|
+
}
|
|
744
|
+
} else {
|
|
745
|
+
var defaultSuffix = buildDefaultNodeIdSuffixFromEditorPath(path);
|
|
746
|
+
var nextSuffix = String(rawVal).trim();
|
|
747
|
+
if (nextSuffix && nextSuffix !== defaultSuffix) {
|
|
748
|
+
customNodeId = "ns=" + nsId + ";s=" + nextSuffix;
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
updateNode(path, { nodeId: customNodeId });
|
|
490
753
|
}
|
|
491
754
|
function normalizeCustomNodeIdFromSuffix(path, suffix) {
|
|
492
755
|
if (isObjectTypeModelPath(path)) return "";
|
|
@@ -527,6 +790,25 @@
|
|
|
527
790
|
}
|
|
528
791
|
|
|
529
792
|
function getChildrenByPath(path) {
|
|
793
|
+
if (path === "virtual:Objects") {
|
|
794
|
+
var children = [];
|
|
795
|
+
(editorState.folders || []).forEach(function (_, i) { children.push("folders." + i); });
|
|
796
|
+
(editorState.objects || []).forEach(function (_, i) { children.push("objects." + i); });
|
|
797
|
+
return children;
|
|
798
|
+
}
|
|
799
|
+
if (path === "virtual:Types") {
|
|
800
|
+
return ["virtual:Types.ObjectTypes", "virtual:Types.DataTypes"];
|
|
801
|
+
}
|
|
802
|
+
if (path === "virtual:Types.ObjectTypes") {
|
|
803
|
+
var children = [];
|
|
804
|
+
(editorState.objectsTypes || []).forEach(function (_, i) { children.push("objectsTypes." + i); });
|
|
805
|
+
return children;
|
|
806
|
+
}
|
|
807
|
+
if (path === "virtual:Types.DataTypes") {
|
|
808
|
+
var children = [];
|
|
809
|
+
(editorState.enumerations || []).forEach(function (_, i) { children.push("enumerations." + i); });
|
|
810
|
+
return children;
|
|
811
|
+
}
|
|
530
812
|
var item = getAtPath(editorState, path);
|
|
531
813
|
if (!item) return [];
|
|
532
814
|
var children = [];
|
|
@@ -540,11 +822,7 @@
|
|
|
540
822
|
}
|
|
541
823
|
|
|
542
824
|
function getTopLevelPaths() {
|
|
543
|
-
var paths = [];
|
|
544
|
-
(editorState.folders || []).forEach(function (_, i) { paths.push("folders." + i); });
|
|
545
|
-
(editorState.objects || []).forEach(function (_, i) { paths.push("objects." + i); });
|
|
546
|
-
(editorState.objectsTypes || []).forEach(function (_, i) { paths.push("objectsTypes." + i); });
|
|
547
|
-
(editorState.enumerations || []).forEach(function (_, i) { paths.push("enumerations." + i); });
|
|
825
|
+
var paths = ["virtual:Objects", "virtual:Types"];
|
|
548
826
|
(editorState.nameSpaces || []).forEach(function (_, i) { paths.push("nameSpaces." + i); });
|
|
549
827
|
return paths;
|
|
550
828
|
}
|
|
@@ -556,6 +834,9 @@
|
|
|
556
834
|
|
|
557
835
|
function nodeMatchesSearch(path) {
|
|
558
836
|
if (!treeSearchTerm) return true;
|
|
837
|
+
if (path && path.indexOf("virtual:") === 0) {
|
|
838
|
+
return getVirtualNodeName(path).toLowerCase().indexOf(treeSearchTerm) !== -1;
|
|
839
|
+
}
|
|
559
840
|
var item = getAtPath(editorState, path);
|
|
560
841
|
if (!item) return false;
|
|
561
842
|
var values = [path, item.name, item.displayName, item.description, nodeClassFromPath(path), item.type, item.value, item.id, item.namespaceId];
|
|
@@ -568,7 +849,7 @@
|
|
|
568
849
|
}
|
|
569
850
|
|
|
570
851
|
function iconForNodeClass(nodeClass) {
|
|
571
|
-
if (nodeClass === "Folder") return "fa-folder";
|
|
852
|
+
if (nodeClass === "Folder" || nodeClass === "VisualFolder") return "fa-folder";
|
|
572
853
|
if (nodeClass === "Object") return "fa-cube";
|
|
573
854
|
if (nodeClass === "Variable") return "fa-tag";
|
|
574
855
|
if (nodeClass === "ObjectType") return "fa-cubes";
|
|
@@ -594,7 +875,7 @@
|
|
|
594
875
|
}
|
|
595
876
|
|
|
596
877
|
function appendNodeToFrag(frag, path, depth, ancestorMatched) {
|
|
597
|
-
var item = getAtPath(editorState, path);
|
|
878
|
+
var item = (path && path.indexOf("virtual:") === 0) ? {} : getAtPath(editorState, path);
|
|
598
879
|
if (!item) return;
|
|
599
880
|
var nodeClass = nodeClassFromPath(path);
|
|
600
881
|
var hasChildren = nodeClass !== "Variable" && nodeClass !== "Alarm" && nodeClass !== "Namespace" && getChildrenByPath(path).length > 0;
|
|
@@ -607,11 +888,16 @@
|
|
|
607
888
|
var row = document.createElement("div");
|
|
608
889
|
row.className = "opcua-tree-row" + (path === selectedPath ? " is-selected" : "");
|
|
609
890
|
row.setAttribute("data-path", path);
|
|
891
|
+
if (path && path.indexOf("virtual:") !== 0 && path.indexOf("nameSpaces.") !== 0) {
|
|
892
|
+
row.setAttribute("draggable", "true");
|
|
893
|
+
}
|
|
894
|
+
var label = (path && path.indexOf("virtual:") === 0) ? getVirtualNodeName(path) : (item.name || "(unnamed)");
|
|
895
|
+
var displayClass = (path && path.indexOf("virtual:") === 0) ? "Folder" : nodeClass;
|
|
610
896
|
row.innerHTML = indents
|
|
611
897
|
+ '<span class="opcua-tree-twisty">' + (hasChildren ? '<i class="fa ' + (expanded ? "fa-caret-down" : "fa-caret-right") + '"></i>' : "") + "</span>"
|
|
612
898
|
+ '<span class="opcua-tree-icon"><i class="fa ' + iconForNodeClass(nodeClass) + '"></i></span>'
|
|
613
|
-
+ '<span class="opcua-tree-label">' + escapeHtml(
|
|
614
|
-
+ '<span class="opcua-tree-type">' + escapeHtml(
|
|
899
|
+
+ '<span class="opcua-tree-label">' + escapeHtml(label) + "</span>"
|
|
900
|
+
+ '<span class="opcua-tree-type">' + escapeHtml(displayClass) + "</span>";
|
|
615
901
|
frag.appendChild(row);
|
|
616
902
|
|
|
617
903
|
if (hasChildren && expanded) {
|
|
@@ -650,9 +936,28 @@
|
|
|
650
936
|
function renderBreadcrumbs() {
|
|
651
937
|
var el = $("#opcua-tree-breadcrumbs");
|
|
652
938
|
if (!selectedPath) { el.text("No selection"); return; }
|
|
939
|
+
if (selectedPath.indexOf("virtual:") === 0) {
|
|
940
|
+
var parts = [];
|
|
941
|
+
if (selectedPath === "virtual:Objects") parts = ["Objects"];
|
|
942
|
+
else if (selectedPath === "virtual:Types") parts = ["Types"];
|
|
943
|
+
else if (selectedPath === "virtual:Types.ObjectTypes") parts = ["Types", "ObjectTypes"];
|
|
944
|
+
else if (selectedPath === "virtual:Types.DataTypes") parts = ["Types", "DataTypes"];
|
|
945
|
+
el.text(parts.join("."));
|
|
946
|
+
return;
|
|
947
|
+
}
|
|
653
948
|
var tokens = pathToTokens(selectedPath);
|
|
654
949
|
var cursor = [];
|
|
655
950
|
var parts = [];
|
|
951
|
+
var rootToken = tokens[0];
|
|
952
|
+
if (rootToken === "folders" || rootToken === "objects") {
|
|
953
|
+
parts.push("Objects");
|
|
954
|
+
} else if (rootToken === "objectsTypes") {
|
|
955
|
+
parts.push("Types");
|
|
956
|
+
parts.push("ObjectTypes");
|
|
957
|
+
} else if (rootToken === "enumerations") {
|
|
958
|
+
parts.push("Types");
|
|
959
|
+
parts.push("DataTypes");
|
|
960
|
+
}
|
|
656
961
|
tokens.forEach(function (token) {
|
|
657
962
|
cursor.push(token);
|
|
658
963
|
if (/^\d+$/.test(token)) parts.push(getNodeDisplayName(cursor.join(".")) || ("#" + token));
|
|
@@ -672,14 +977,38 @@
|
|
|
672
977
|
|
|
673
978
|
function openCreateForm(path, kind) {
|
|
674
979
|
if (!path) return;
|
|
675
|
-
if (
|
|
980
|
+
if (path.indexOf("virtual:") === 0) {
|
|
981
|
+
if (path === "virtual:Objects") {
|
|
982
|
+
if (kind !== "folder" && kind !== "object") {
|
|
983
|
+
RED.notify("Only Folders and Objects can be added directly under Objects", "warning");
|
|
984
|
+
return;
|
|
985
|
+
}
|
|
986
|
+
} else if (path === "virtual:Types.ObjectTypes") {
|
|
987
|
+
if (kind !== "objecttype") {
|
|
988
|
+
RED.notify("Only ObjectTypes can be added under ObjectTypes", "warning");
|
|
989
|
+
return;
|
|
990
|
+
}
|
|
991
|
+
} else if (path === "virtual:Types.DataTypes") {
|
|
992
|
+
if (kind !== "enumeration") {
|
|
993
|
+
RED.notify("Only Enumerations can be added under DataTypes", "warning");
|
|
994
|
+
return;
|
|
995
|
+
}
|
|
996
|
+
} else {
|
|
997
|
+
RED.notify("Cannot add children to this visual folder", "warning");
|
|
998
|
+
return;
|
|
999
|
+
}
|
|
1000
|
+
} else if (nodeClassFromPath(path) === "Variable" || nodeClassFromPath(path) === "Namespace") {
|
|
676
1001
|
RED.notify("Selected item cannot have children", "warning");
|
|
677
1002
|
return;
|
|
678
1003
|
}
|
|
1004
|
+
var parentSuffix = path.indexOf("virtual:") === 0 ? "" : buildDefaultNodeIdSuffixFromEditorPath(path);
|
|
1005
|
+
var defaultName = kind === "variable" ? "newVariable" : kind === "enum-variable" ? "newEnumVariable" : kind === "folder" ? "newFolder" : kind === "method" ? "newMethod" : kind === "alarm" ? "newAlarm" : "newObject";
|
|
1006
|
+
var defaultSuffix = parentSuffix ? parentSuffix + "." + defaultName : defaultName;
|
|
1007
|
+
|
|
679
1008
|
pendingCreate = {
|
|
680
1009
|
parentPath: path,
|
|
681
1010
|
kind: kind,
|
|
682
|
-
name:
|
|
1011
|
+
name: defaultName,
|
|
683
1012
|
displayName: "",
|
|
684
1013
|
dataType: kind === "enum-variable" ? (getDefinedEnumerationNames()[0] || "") : "Int32",
|
|
685
1014
|
value: "",
|
|
@@ -689,16 +1018,19 @@
|
|
|
689
1018
|
alarmType: "levelAlarm",
|
|
690
1019
|
variableNodeId: "",
|
|
691
1020
|
severity: 500,
|
|
692
|
-
|
|
1021
|
+
sendValue: true,
|
|
1022
|
+
highHighLimit: 90,
|
|
693
1023
|
highHighMessage: "High High alarm",
|
|
694
1024
|
highLimit: 80,
|
|
695
1025
|
highMessage: "High alarm",
|
|
696
1026
|
lowLimit: 20,
|
|
697
1027
|
lowMessage: "Low alarm",
|
|
698
|
-
lowLowLimit:
|
|
1028
|
+
lowLowLimit: 10,
|
|
699
1029
|
lowLowMessage: "Low Low alarm",
|
|
700
1030
|
normalStateValue: 0,
|
|
701
|
-
digitalMessage: "Digital alarm"
|
|
1031
|
+
digitalMessage: "Digital alarm",
|
|
1032
|
+
nodeIdType: "s",
|
|
1033
|
+
nodeIdValue: defaultSuffix
|
|
702
1034
|
};
|
|
703
1035
|
renderDetails();
|
|
704
1036
|
}
|
|
@@ -707,19 +1039,45 @@
|
|
|
707
1039
|
if (!pendingCreate) return;
|
|
708
1040
|
var parentPath = pendingCreate.parentPath;
|
|
709
1041
|
var kind = pendingCreate.kind;
|
|
710
|
-
var branchTargetPath
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
1042
|
+
var branchTargetPath;
|
|
1043
|
+
if (parentPath.indexOf("virtual:") === 0) {
|
|
1044
|
+
if (parentPath === "virtual:Objects") {
|
|
1045
|
+
branchTargetPath = kind === "folder" ? "folders" : "objects";
|
|
1046
|
+
} else if (parentPath === "virtual:Types.ObjectTypes") {
|
|
1047
|
+
branchTargetPath = "objectsTypes";
|
|
1048
|
+
} else {
|
|
1049
|
+
return;
|
|
1050
|
+
}
|
|
1051
|
+
} else {
|
|
1052
|
+
branchTargetPath = (kind === "variable" || kind === "enum-variable")
|
|
1053
|
+
? (parentPath + ".variables")
|
|
1054
|
+
: kind === "folder"
|
|
1055
|
+
? (parentPath + ".folders")
|
|
1056
|
+
: kind === "objecttype"
|
|
1057
|
+
? (parentPath + ".objectsTypes")
|
|
1058
|
+
: kind === "alarm"
|
|
1059
|
+
? (parentPath + ".alarms")
|
|
1060
|
+
: kind === "method"
|
|
1061
|
+
? (parentPath + ".methods")
|
|
1062
|
+
: (parentPath + ".objects");
|
|
1063
|
+
}
|
|
721
1064
|
var target = getAtPath(editorState, branchTargetPath);
|
|
722
1065
|
if (!Array.isArray(target)) return;
|
|
1066
|
+
|
|
1067
|
+
var customNodeId = "";
|
|
1068
|
+
var parentIsObjectTypeModel = isObjectTypeModelPath(parentPath);
|
|
1069
|
+
if (!parentIsObjectTypeModel && pendingCreate.nodeIdValue) {
|
|
1070
|
+
var nsId = getNodeNamespaceId(parentPath);
|
|
1071
|
+
if (pendingCreate.nodeIdType === "i") {
|
|
1072
|
+
var numVal = parseInt(pendingCreate.nodeIdValue, 10);
|
|
1073
|
+
if (!isNaN(numVal)) {
|
|
1074
|
+
customNodeId = "ns=" + nsId + ";i=" + numVal;
|
|
1075
|
+
}
|
|
1076
|
+
} else {
|
|
1077
|
+
customNodeId = "ns=" + nsId + ";s=" + pendingCreate.nodeIdValue.trim();
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
|
|
723
1081
|
if (kind === "variable" || kind === "enum-variable") {
|
|
724
1082
|
target.push(normalizeVariable({
|
|
725
1083
|
name: pendingCreate.name,
|
|
@@ -727,13 +1085,16 @@
|
|
|
727
1085
|
type: pendingCreate.dataType,
|
|
728
1086
|
value: pendingCreate.value,
|
|
729
1087
|
access: pendingCreate.access || "readwrite",
|
|
730
|
-
accessPermission: pendingCreate.accessPermission
|
|
1088
|
+
accessPermission: pendingCreate.accessPermission,
|
|
1089
|
+
nodeId: customNodeId
|
|
731
1090
|
}));
|
|
732
1091
|
} else if (kind === "folder") {
|
|
733
|
-
target.push(normalizeBranch({ name: pendingCreate.name, displayName: pendingCreate.displayName || "", accessPermission: pendingCreate.accessPermission }));
|
|
1092
|
+
target.push(normalizeBranch({ name: pendingCreate.name, displayName: pendingCreate.displayName || "", accessPermission: pendingCreate.accessPermission, nodeId: customNodeId }));
|
|
734
1093
|
} else if (kind === "objecttype") {
|
|
735
|
-
target.push(normalizeBranch({ name: pendingCreate.name, displayName: pendingCreate.displayName || "", objectsType: pendingCreate.objectsType || "", accessPermission: pendingCreate.accessPermission }));
|
|
736
|
-
|
|
1094
|
+
target.push(normalizeBranch({ name: pendingCreate.name, displayName: pendingCreate.displayName || "", objectsType: pendingCreate.objectsType || "", accessPermission: pendingCreate.accessPermission, nodeId: customNodeId }));
|
|
1095
|
+
if (!customNodeId) {
|
|
1096
|
+
target[target.length - 1].nodeId = buildGeneratedNodeIdForPath(branchTargetPath + "." + (target.length - 1));
|
|
1097
|
+
}
|
|
737
1098
|
} else if (kind === "alarm") {
|
|
738
1099
|
target.push(normalizeAlarm({
|
|
739
1100
|
displayName: pendingCreate.displayName || "",
|
|
@@ -742,6 +1103,7 @@
|
|
|
742
1103
|
type: pendingCreate.alarmType,
|
|
743
1104
|
variableNodeId: pendingCreate.variableNodeId,
|
|
744
1105
|
severity: Number(pendingCreate.severity || 500),
|
|
1106
|
+
sendValue: pendingCreate.sendValue,
|
|
745
1107
|
highHighLimit: pendingCreate.highHighLimit,
|
|
746
1108
|
highHighMessage: pendingCreate.highHighMessage,
|
|
747
1109
|
highLimit: pendingCreate.highLimit,
|
|
@@ -751,12 +1113,13 @@
|
|
|
751
1113
|
lowLowLimit: pendingCreate.lowLowLimit,
|
|
752
1114
|
lowLowMessage: pendingCreate.lowLowMessage,
|
|
753
1115
|
normalStateValue: pendingCreate.normalStateValue,
|
|
754
|
-
digitalMessage: pendingCreate.digitalMessage
|
|
1116
|
+
digitalMessage: pendingCreate.digitalMessage,
|
|
1117
|
+
nodeId: customNodeId
|
|
755
1118
|
}));
|
|
756
1119
|
} else if (kind === "method") {
|
|
757
|
-
target.push(normalizeMethod({ name: pendingCreate.name, displayName: pendingCreate.displayName || "", accessPermission: pendingCreate.accessPermission }));
|
|
1120
|
+
target.push(normalizeMethod({ name: pendingCreate.name, displayName: pendingCreate.displayName || "", accessPermission: pendingCreate.accessPermission, nodeId: customNodeId }));
|
|
758
1121
|
} else {
|
|
759
|
-
target.push(normalizeBranch({ name: pendingCreate.name, displayName: pendingCreate.displayName || "", accessPermission: pendingCreate.accessPermission }));
|
|
1122
|
+
target.push(normalizeBranch({ name: pendingCreate.name, displayName: pendingCreate.displayName || "", accessPermission: pendingCreate.accessPermission, nodeId: customNodeId }));
|
|
760
1123
|
}
|
|
761
1124
|
expansionState[parentPath] = true;
|
|
762
1125
|
pendingCreate = null;
|
|
@@ -778,6 +1141,11 @@
|
|
|
778
1141
|
panel.append('<div class="form-row"><label>Name</label><input type="text" id="opcua-create-name"></div>');
|
|
779
1142
|
panel.append('<div class="form-row"><label>displayName</label><input type="text" id="opcua-create-displayname" placeholder="Leave blank to use browseName"></div>');
|
|
780
1143
|
panel.append('<div class="form-row"><label>accessPermission</label>' + buildAccessPermissionSelect("opcua-create-accesspermission", pendingCreate.accessPermission || ["public"]) + '</div>');
|
|
1144
|
+
var parentIsObjectTypeModel = isObjectTypeModelPath(pendingCreate.parentPath);
|
|
1145
|
+
if (!parentIsObjectTypeModel) {
|
|
1146
|
+
panel.append('<div class="form-row"><label>nodeId Type</label><select id="opcua-create-nodeid-type"><option value="s">s (String)</option><option value="i">i (Numeric)</option></select></div>');
|
|
1147
|
+
panel.append('<div class="form-row" id="opcua-create-nodeid-value-row"><label id="opcua-create-nodeid-value-label">nodeId Value</label><input type="text" id="opcua-create-nodeid-value" placeholder="Leave blank for default (s)"></div>');
|
|
1148
|
+
}
|
|
781
1149
|
if (pendingCreate.kind === "variable" || pendingCreate.kind === "enum-variable") {
|
|
782
1150
|
var isEnum = pendingCreate.kind === "enum-variable";
|
|
783
1151
|
var typeHtml = isEnum ? buildEnumerationSelect("opcua-create-type", pendingCreate.dataType) : '<select id="opcua-create-type"><option value="Int16">Int16</option><option value="Int32">Int32</option><option value="Int64">Int64</option><option value="Float">Float</option><option value="Boolean">Boolean</option><option value="String">String</option></select>';
|
|
@@ -792,6 +1160,7 @@
|
|
|
792
1160
|
panel.append('<div class="form-row"><label>alarmType</label><select id="opcua-create-alarm-type"><option value="levelAlarm">levelAlarm</option><option value="digitalAlarm">digitalAlarm</option></select></div>');
|
|
793
1161
|
panel.append('<div class="form-row"><label>variablePath</label><input type="text" id="opcua-create-variable-nodeid"></div>');
|
|
794
1162
|
panel.append('<div class="form-row"><label>severity</label><input type="number" id="opcua-create-severity"></div>');
|
|
1163
|
+
panel.append('<div class="form-row"><label for="opcua-create-sendvalue">Send alarm value</label><input type="checkbox" id="opcua-create-sendvalue" style="width: auto; flex: 0 0 auto; min-width: 0;"></div>');
|
|
795
1164
|
if (pendingCreate.alarmType === "levelAlarm") {
|
|
796
1165
|
panel.append('<div class="form-row"><label>highHighLimit</label><input type="number" id="opcua-create-highhighlimit"></div>');
|
|
797
1166
|
panel.append('<div class="form-row"><label>highHighMessage</label><input type="text" id="opcua-create-highhighmessage"></div>');
|
|
@@ -812,6 +1181,11 @@
|
|
|
812
1181
|
$("#opcua-create-name").val(pendingCreate.name);
|
|
813
1182
|
$("#opcua-create-displayname").val(pendingCreate.displayName || "");
|
|
814
1183
|
$("#opcua-create-accesspermission").val(normalizeAccessPermissionValues(pendingCreate.accessPermission));
|
|
1184
|
+
if (!parentIsObjectTypeModel) {
|
|
1185
|
+
$("#opcua-create-nodeid-type").val(pendingCreate.nodeIdType || "s");
|
|
1186
|
+
$("#opcua-create-nodeid-value").val(pendingCreate.nodeIdValue || "");
|
|
1187
|
+
updateNodeIdValueInputState("create", pendingCreate.nodeIdType || "s");
|
|
1188
|
+
}
|
|
815
1189
|
$("#opcua-create-type").val(pendingCreate.dataType);
|
|
816
1190
|
$("#opcua-create-value").val(pendingCreate.value);
|
|
817
1191
|
$("#opcua-create-objectstype").val(pendingCreate.objectsType);
|
|
@@ -819,6 +1193,7 @@
|
|
|
819
1193
|
$("#opcua-create-alarm-type").val(pendingCreate.alarmType);
|
|
820
1194
|
$("#opcua-create-variable-nodeid").val(pendingCreate.variableNodeId);
|
|
821
1195
|
$("#opcua-create-severity").val(pendingCreate.severity);
|
|
1196
|
+
$("#opcua-create-sendvalue").prop("checked", pendingCreate.sendValue !== false);
|
|
822
1197
|
$("#opcua-create-highhighlimit").val(pendingCreate.highHighLimit);
|
|
823
1198
|
$("#opcua-create-highhighmessage").val(pendingCreate.highHighMessage);
|
|
824
1199
|
$("#opcua-create-highlimit").val(pendingCreate.highLimit);
|
|
@@ -832,6 +1207,11 @@
|
|
|
832
1207
|
return;
|
|
833
1208
|
}
|
|
834
1209
|
if (!selectedPath) { panel.append('<div class="opcua-tree-empty">Select a node to edit browseName, namespace, nodeId, and description.</div>'); return; }
|
|
1210
|
+
if (selectedPath.indexOf("virtual:") === 0) {
|
|
1211
|
+
var visualName = getVirtualNodeName(selectedPath);
|
|
1212
|
+
panel.append('<div class="opcua-tree-empty">Visual folder: <strong>' + escapeHtml(visualName) + '</strong><br><span style="font-size: 11px; color: #666;">This folder is used strictly for visual organization within the modal.</span></div>');
|
|
1213
|
+
return;
|
|
1214
|
+
}
|
|
835
1215
|
var item = getAtPath(editorState, selectedPath);
|
|
836
1216
|
if (!item) { panel.append('<div class="opcua-tree-empty">Selected node not found.</div>'); return; }
|
|
837
1217
|
var nodeClass = nodeClassFromPath(selectedPath);
|
|
@@ -883,7 +1263,18 @@
|
|
|
883
1263
|
panel.append('<div class="form-row"><label>browseName</label><input type="text" id="opcua-detail-name"></div>');
|
|
884
1264
|
panel.append('<div class="form-row"><label>nodeClass</label><input type="text" id="opcua-detail-class" readonly></div>');
|
|
885
1265
|
panel.append('<div class="form-row"><label>namespace</label><select id="opcua-detail-namespace"></select></div>');
|
|
886
|
-
|
|
1266
|
+
if (!nodeIdLocked) {
|
|
1267
|
+
panel.append('<div class="form-row"><label>nodeId</label>' +
|
|
1268
|
+
'<div class="opcua-nodeid-field">' +
|
|
1269
|
+
'<span class="opcua-nodeid-prefix">ns=' + namespaceId + ';</span>' +
|
|
1270
|
+
'<select id="opcua-detail-nodeid-type" style="width: 70px; flex: 0 0 auto;"><option value="s">s</option><option value="i">i</option></select>' +
|
|
1271
|
+
'<span style="padding: 0 4px; flex: 0 0 auto;">=</span>' +
|
|
1272
|
+
'<input type="text" id="opcua-detail-nodeid-value" style="flex: 1 1 auto; font-family: monospace;">' +
|
|
1273
|
+
'<a href="#" id="opcua-detail-copy-nodeid" class="editor-button editor-button-small"><i class="fa fa-copy"></i> Copy</a>' +
|
|
1274
|
+
'</div></div>');
|
|
1275
|
+
} else {
|
|
1276
|
+
panel.append('<div class="form-row"><label>nodeId</label><div class="opcua-nodeid-field"><span class="opcua-nodeid-prefix">' + getNodeIdPrefix(namespaceId) + '</span><input type="text" id="opcua-detail-nodeid"' + (nodeIdLocked ? ' readonly title="Generated automatically for object type models."' : '') + '><a href="#" id="opcua-detail-copy-nodeid" class="editor-button editor-button-small"><i class="fa fa-copy"></i> Copy</a></div></div>');
|
|
1277
|
+
}
|
|
887
1278
|
panel.append('<div class="form-row"><label>Description</label><input type="text" id="opcua-detail-description"></div>');
|
|
888
1279
|
panel.append('<div class="form-row"><label>displayName</label><input type="text" id="opcua-detail-displayname" placeholder="Leave blank to use browseName"></div>');
|
|
889
1280
|
panel.append('<div class="form-row"><label>accessPermission</label>' + buildAccessPermissionSelect("opcua-detail-accesspermission", item.accessPermission || ["public"]) + '</div>');
|
|
@@ -942,6 +1333,7 @@
|
|
|
942
1333
|
panel.append('<div class="form-row"><label>alarmType</label><select id="opcua-detail-alarm-type"><option value="levelAlarm">levelAlarm</option><option value="digitalAlarm">digitalAlarm</option></select></div>');
|
|
943
1334
|
panel.append('<div class="form-row"><label>variablePath</label><input type="text" id="opcua-detail-variable-nodeid"></div>');
|
|
944
1335
|
panel.append('<div class="form-row"><label>severity</label><input type="number" id="opcua-detail-severity"></div>');
|
|
1336
|
+
panel.append('<div class="form-row"><label for="opcua-detail-sendvalue">Send alarm value</label><input type="checkbox" id="opcua-detail-sendvalue" style="width: auto; flex: 0 0 auto; min-width: 0;"></div>');
|
|
945
1337
|
if ((item.type || "levelAlarm") === "levelAlarm") {
|
|
946
1338
|
panel.append('<div class="form-row"><label>highHighLimit</label><input type="number" id="opcua-detail-highhighlimit"></div>');
|
|
947
1339
|
panel.append('<div class="form-row"><label>highHighMessage</label><input type="text" id="opcua-detail-highhighmessage"></div>');
|
|
@@ -959,7 +1351,14 @@
|
|
|
959
1351
|
panel.append('<div class="form-row"><label style="width:90px;">Actions</label><div><a href="#" id="opcua-detail-edit" class="editor-button editor-button-small"><i class="fa fa-pencil"></i> Edit</a> <a href="#" id="opcua-detail-remove" class="editor-button editor-button-small"><i class="fa fa-trash"></i> Remove</a></div></div>');
|
|
960
1352
|
$("#opcua-detail-name").val(item.name || "");
|
|
961
1353
|
$("#opcua-detail-class").val(nodeClass);
|
|
962
|
-
|
|
1354
|
+
if (!nodeIdLocked) {
|
|
1355
|
+
var parsed = parseNodeId(item.nodeId, buildDefaultNodeIdSuffixFromEditorPath(selectedPath));
|
|
1356
|
+
$("#opcua-detail-nodeid-type").val(parsed.type);
|
|
1357
|
+
$("#opcua-detail-nodeid-value").val(parsed.value);
|
|
1358
|
+
updateNodeIdValueInputState("detail", parsed.type);
|
|
1359
|
+
} else {
|
|
1360
|
+
$("#opcua-detail-nodeid").val(nodeIdLocked ? buildDefaultNodeIdSuffixFromEditorPath(selectedPath) : nodeIdSuffix);
|
|
1361
|
+
}
|
|
963
1362
|
$("#opcua-detail-description").val(item.description || "");
|
|
964
1363
|
namespaceOptions.forEach(function (option) {
|
|
965
1364
|
$("#opcua-detail-namespace").append($("<option></option>").val(option.id).text(getNamespaceLabel(option.id)));
|
|
@@ -973,13 +1372,14 @@
|
|
|
973
1372
|
$("#opcua-detail-alarm-type").val(item.type || "levelAlarm");
|
|
974
1373
|
$("#opcua-detail-variable-nodeid").val(item.variableNodeId || "");
|
|
975
1374
|
$("#opcua-detail-severity").val(item.severity !== undefined ? item.severity : 500);
|
|
976
|
-
$("#opcua-detail-
|
|
1375
|
+
$("#opcua-detail-sendvalue").prop("checked", item.sendValue !== false);
|
|
1376
|
+
$("#opcua-detail-highhighlimit").val(item.highHighLimit !== undefined ? item.highHighLimit : 90);
|
|
977
1377
|
$("#opcua-detail-highhighmessage").val(item.highHighMessage || "High High alarm");
|
|
978
1378
|
$("#opcua-detail-highlimit").val(item.highLimit !== undefined ? item.highLimit : 80);
|
|
979
1379
|
$("#opcua-detail-highmessage").val(item.highMessage || "High alarm");
|
|
980
1380
|
$("#opcua-detail-lowlimit").val(item.lowLimit !== undefined ? item.lowLimit : 20);
|
|
981
1381
|
$("#opcua-detail-lowmessage").val(item.lowMessage || "Low alarm");
|
|
982
|
-
$("#opcua-detail-lowlowlimit").val(item.lowLowLimit !== undefined ? item.lowLowLimit :
|
|
1382
|
+
$("#opcua-detail-lowlowlimit").val(item.lowLowLimit !== undefined ? item.lowLowLimit : 10);
|
|
983
1383
|
$("#opcua-detail-lowlowmessage").val(item.lowLowMessage || "Low Low alarm");
|
|
984
1384
|
$("#opcua-detail-normalstatevalue").val(item.normalStateValue !== undefined ? item.normalStateValue : 0);
|
|
985
1385
|
$("#opcua-detail-digitalmessage").val(item.digitalMessage || "Digital alarm");
|
|
@@ -993,6 +1393,10 @@
|
|
|
993
1393
|
|
|
994
1394
|
function removeNode(path) {
|
|
995
1395
|
if (!path) return;
|
|
1396
|
+
if (path.indexOf("virtual:") === 0) {
|
|
1397
|
+
RED.notify("Visual folders cannot be removed.", "warning");
|
|
1398
|
+
return;
|
|
1399
|
+
}
|
|
996
1400
|
if (nodeClassFromPath(path) === "Namespace") {
|
|
997
1401
|
var namespaceItem = getAtPath(editorState, path);
|
|
998
1402
|
if (normalizeNamespaceId(namespaceItem && namespaceItem.id) === DEFAULT_NAMESPACE_ID) {
|
|
@@ -1004,8 +1408,72 @@
|
|
|
1004
1408
|
if (selectedPath === path) selectedPath = "";
|
|
1005
1409
|
syncStateToJson(true);
|
|
1006
1410
|
renderVisualEditor();
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
function moveNode(srcPath, destPath) {
|
|
1414
|
+
var item = getAtPath(editorState, srcPath);
|
|
1415
|
+
if (!item) return;
|
|
1416
|
+
|
|
1417
|
+
var srcClass = nodeClassFromPath(srcPath);
|
|
1418
|
+
var targetCollection = null;
|
|
1419
|
+
|
|
1420
|
+
if (destPath === "virtual:Objects") {
|
|
1421
|
+
if (srcClass === "Folder") {
|
|
1422
|
+
editorState.folders = editorState.folders || [];
|
|
1423
|
+
targetCollection = editorState.folders;
|
|
1424
|
+
} else if (srcClass === "Object") {
|
|
1425
|
+
editorState.objects = editorState.objects || [];
|
|
1426
|
+
targetCollection = editorState.objects;
|
|
1427
|
+
}
|
|
1428
|
+
} else if (destPath === "virtual:Types.ObjectTypes") {
|
|
1429
|
+
editorState.objectsTypes = editorState.objectsTypes || [];
|
|
1430
|
+
targetCollection = editorState.objectsTypes;
|
|
1431
|
+
} else if (destPath === "virtual:Types.DataTypes") {
|
|
1432
|
+
editorState.enumerations = editorState.enumerations || [];
|
|
1433
|
+
targetCollection = editorState.enumerations;
|
|
1434
|
+
} else {
|
|
1435
|
+
var destParent = getAtPath(editorState, destPath);
|
|
1436
|
+
if (!destParent) return;
|
|
1437
|
+
|
|
1438
|
+
if (srcClass === "Folder") {
|
|
1439
|
+
destParent.folders = destParent.folders || [];
|
|
1440
|
+
targetCollection = destParent.folders;
|
|
1441
|
+
} else if (srcClass === "Object") {
|
|
1442
|
+
destParent.objects = destParent.objects || [];
|
|
1443
|
+
targetCollection = destParent.objects;
|
|
1444
|
+
} else if (srcClass === "Variable") {
|
|
1445
|
+
destParent.variables = destParent.variables || [];
|
|
1446
|
+
targetCollection = destParent.variables;
|
|
1447
|
+
} else if (srcClass === "Method") {
|
|
1448
|
+
destParent.methods = destParent.methods || [];
|
|
1449
|
+
targetCollection = destParent.methods;
|
|
1450
|
+
} else if (srcClass === "Alarm") {
|
|
1451
|
+
destParent.alarms = destParent.alarms || [];
|
|
1452
|
+
targetCollection = destParent.alarms;
|
|
1453
|
+
} else if (srcClass === "ObjectType") {
|
|
1454
|
+
destParent.objectsTypes = destParent.objectsTypes || [];
|
|
1455
|
+
targetCollection = destParent.objectsTypes;
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
if (targetCollection) {
|
|
1460
|
+
removeAtPath(editorState, srcPath);
|
|
1461
|
+
targetCollection.push(item);
|
|
1462
|
+
|
|
1463
|
+
var newPath = findObjectPath(editorState, item, "");
|
|
1464
|
+
if (newPath) {
|
|
1465
|
+
selectedPath = newPath;
|
|
1466
|
+
expansionState[destPath] = true;
|
|
1467
|
+
} else {
|
|
1468
|
+
selectedPath = "";
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
syncStateToJson(true);
|
|
1472
|
+
renderVisualEditor();
|
|
1473
|
+
}
|
|
1007
1474
|
}
|
|
1008
1475
|
|
|
1476
|
+
|
|
1009
1477
|
function buildGroupOptions(selectedGroup) {
|
|
1010
1478
|
var currentGroup = String(selectedGroup || "");
|
|
1011
1479
|
var options = authGroups.map(function (groupName) {
|
|
@@ -1234,6 +1702,56 @@
|
|
|
1234
1702
|
} catch (error) { RED.notify("Invalid JSON: " + error.message, "error"); }
|
|
1235
1703
|
});
|
|
1236
1704
|
|
|
1705
|
+
$(document).on("dragstart", ".opcua-tree-row", function (event) {
|
|
1706
|
+
var path = $(this).attr("data-path");
|
|
1707
|
+
if (path && path.indexOf("virtual:") !== 0 && path.indexOf("nameSpaces.") !== 0) {
|
|
1708
|
+
draggedPath = path;
|
|
1709
|
+
if (event.originalEvent.dataTransfer) {
|
|
1710
|
+
event.originalEvent.dataTransfer.effectAllowed = "move";
|
|
1711
|
+
event.originalEvent.dataTransfer.setData("text/plain", path);
|
|
1712
|
+
}
|
|
1713
|
+
$(this).addClass("is-dragging");
|
|
1714
|
+
} else {
|
|
1715
|
+
event.preventDefault();
|
|
1716
|
+
}
|
|
1717
|
+
});
|
|
1718
|
+
|
|
1719
|
+
$(document).on("dragend", ".opcua-tree-row", function (event) {
|
|
1720
|
+
$(this).removeClass("is-dragging");
|
|
1721
|
+
draggedPath = null;
|
|
1722
|
+
$(".opcua-tree-row").removeClass("drag-over");
|
|
1723
|
+
});
|
|
1724
|
+
|
|
1725
|
+
$(document).on("dragover", ".opcua-tree-row", function (event) {
|
|
1726
|
+
if (!draggedPath) return;
|
|
1727
|
+
event.preventDefault();
|
|
1728
|
+
|
|
1729
|
+
var row = $(this);
|
|
1730
|
+
var targetPath = row.attr("data-path");
|
|
1731
|
+
if (isValidDropTarget(draggedPath, targetPath)) {
|
|
1732
|
+
if (event.originalEvent.dataTransfer) {
|
|
1733
|
+
event.originalEvent.dataTransfer.dropEffect = "move";
|
|
1734
|
+
}
|
|
1735
|
+
$(".opcua-tree-row").not(row).removeClass("drag-over");
|
|
1736
|
+
row.addClass("drag-over");
|
|
1737
|
+
}
|
|
1738
|
+
});
|
|
1739
|
+
|
|
1740
|
+
$(document).on("dragleave", ".opcua-tree-row", function (event) {
|
|
1741
|
+
$(this).removeClass("drag-over");
|
|
1742
|
+
});
|
|
1743
|
+
|
|
1744
|
+
$(document).on("drop", ".opcua-tree-row", function (event) {
|
|
1745
|
+
event.preventDefault();
|
|
1746
|
+
$(".opcua-tree-row").removeClass("drag-over");
|
|
1747
|
+
|
|
1748
|
+
if (!draggedPath) return;
|
|
1749
|
+
var targetPath = $(this).attr("data-path");
|
|
1750
|
+
if (isValidDropTarget(draggedPath, targetPath)) {
|
|
1751
|
+
moveNode(draggedPath, targetPath);
|
|
1752
|
+
}
|
|
1753
|
+
});
|
|
1754
|
+
|
|
1237
1755
|
$(document).on("click", ".opcua-tree-row", function (event) {
|
|
1238
1756
|
var path = $(this).attr("data-path");
|
|
1239
1757
|
if ($(event.target).closest(".opcua-tree-twisty").length) {
|
|
@@ -1246,8 +1764,57 @@
|
|
|
1246
1764
|
|
|
1247
1765
|
$(document).on("contextmenu", ".opcua-tree-row", function (event) {
|
|
1248
1766
|
event.preventDefault();
|
|
1249
|
-
|
|
1250
|
-
|
|
1767
|
+
var path = $(this).attr("data-path");
|
|
1768
|
+
selectNode(path);
|
|
1769
|
+
|
|
1770
|
+
var contextMenu = $("#opcua-tree-context-menu");
|
|
1771
|
+
contextMenu.find("a").hide();
|
|
1772
|
+
|
|
1773
|
+
if (path.indexOf("virtual:") === 0) {
|
|
1774
|
+
if (path === "virtual:Objects") {
|
|
1775
|
+
contextMenu.find('[data-action="add-folder"]').show();
|
|
1776
|
+
contextMenu.find('[data-action="add-object"]').show();
|
|
1777
|
+
contextMenu.find('[data-action="expand-all-below"]').show();
|
|
1778
|
+
contextMenu.find('[data-action="collapse-all-below"]').show();
|
|
1779
|
+
} else if (path === "virtual:Types.ObjectTypes") {
|
|
1780
|
+
contextMenu.find('[data-action="add-objecttype"]').show();
|
|
1781
|
+
contextMenu.find('[data-action="expand-all-below"]').show();
|
|
1782
|
+
contextMenu.find('[data-action="collapse-all-below"]').show();
|
|
1783
|
+
} else if (path === "virtual:Types.DataTypes") {
|
|
1784
|
+
contextMenu.find('[data-action="add-enumeration"]').show();
|
|
1785
|
+
contextMenu.find('[data-action="expand-all-below"]').show();
|
|
1786
|
+
contextMenu.find('[data-action="collapse-all-below"]').show();
|
|
1787
|
+
}
|
|
1788
|
+
} else {
|
|
1789
|
+
var nodeClass = nodeClassFromPath(path);
|
|
1790
|
+
if (nodeClass === "Folder" || nodeClass === "Object" || nodeClass === "ObjectType") {
|
|
1791
|
+
contextMenu.find('[data-action="add-folder"]').show();
|
|
1792
|
+
contextMenu.find('[data-action="add-object"]').show();
|
|
1793
|
+
contextMenu.find('[data-action="add-variable"]').show();
|
|
1794
|
+
contextMenu.find('[data-action="add-objecttype"]').show();
|
|
1795
|
+
contextMenu.find('[data-action="add-enum-variable"]').show();
|
|
1796
|
+
contextMenu.find('[data-action="add-alarm"]').show();
|
|
1797
|
+
contextMenu.find('[data-action="add-method"]').show();
|
|
1798
|
+
contextMenu.find('[data-action="edit"]').show();
|
|
1799
|
+
contextMenu.find('[data-action="remove"]').show();
|
|
1800
|
+
contextMenu.find('[data-action="expand-all-below"]').show();
|
|
1801
|
+
contextMenu.find('[data-action="collapse-all-below"]').show();
|
|
1802
|
+
} else if (nodeClass === "Enumeration") {
|
|
1803
|
+
contextMenu.find('[data-action="edit"]').show();
|
|
1804
|
+
contextMenu.find('[data-action="remove"]').show();
|
|
1805
|
+
} else if (nodeClass === "Namespace") {
|
|
1806
|
+
contextMenu.find('[data-action="edit"]').show();
|
|
1807
|
+
var item = getAtPath(editorState, path);
|
|
1808
|
+
if (item && normalizeNamespaceId(item.id) !== DEFAULT_NAMESPACE_ID) {
|
|
1809
|
+
contextMenu.find('[data-action="remove"]').show();
|
|
1810
|
+
}
|
|
1811
|
+
} else {
|
|
1812
|
+
contextMenu.find('[data-action="edit"]').show();
|
|
1813
|
+
contextMenu.find('[data-action="remove"]').show();
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1817
|
+
contextMenu.css({ left: event.clientX + "px", top: event.clientY + "px" }).show();
|
|
1251
1818
|
});
|
|
1252
1819
|
|
|
1253
1820
|
$(document).on("click", function () { $("#opcua-tree-context-menu").hide(); });
|
|
@@ -1265,6 +1832,22 @@
|
|
|
1265
1832
|
if (action === "add-method") addNode(selectedPath, "method");
|
|
1266
1833
|
if (action === "remove") removeNode(selectedPath);
|
|
1267
1834
|
if (action === "edit") renderDetails();
|
|
1835
|
+
if (action === "expand-all-below") {
|
|
1836
|
+
function walk(p) {
|
|
1837
|
+
expansionState[p] = true;
|
|
1838
|
+
getChildrenByPath(p).forEach(walk);
|
|
1839
|
+
}
|
|
1840
|
+
walk(selectedPath);
|
|
1841
|
+
renderTree();
|
|
1842
|
+
}
|
|
1843
|
+
if (action === "collapse-all-below") {
|
|
1844
|
+
function walk(p) {
|
|
1845
|
+
expansionState[p] = false;
|
|
1846
|
+
getChildrenByPath(p).forEach(walk);
|
|
1847
|
+
}
|
|
1848
|
+
walk(selectedPath);
|
|
1849
|
+
renderTree();
|
|
1850
|
+
}
|
|
1268
1851
|
$("#opcua-tree-context-menu").hide();
|
|
1269
1852
|
});
|
|
1270
1853
|
|
|
@@ -1344,13 +1927,25 @@
|
|
|
1344
1927
|
var item = getAtPath(editorState, selectedPath);
|
|
1345
1928
|
if (!item) return;
|
|
1346
1929
|
item.namespaceId = nextNamespaceId;
|
|
1347
|
-
|
|
1930
|
+
if (nodeClassFromPath(selectedPath) === "Variable" && !isObjectTypeModelPath(selectedPath)) {
|
|
1931
|
+
saveDetailNodeId(selectedPath);
|
|
1932
|
+
} else {
|
|
1933
|
+
item.nodeId = normalizeCustomNodeIdFromSuffix(selectedPath, $("#opcua-detail-nodeid").val());
|
|
1934
|
+
}
|
|
1348
1935
|
syncStateToJson(false);
|
|
1349
1936
|
renderTree();
|
|
1350
1937
|
renderBreadcrumbs();
|
|
1351
1938
|
renderDetails();
|
|
1352
1939
|
});
|
|
1353
1940
|
$(document).on("input", "#opcua-detail-nodeid", function () { updateNode(selectedPath, { nodeId: normalizeCustomNodeIdFromSuffix(selectedPath, $(this).val()) }); });
|
|
1941
|
+
$(document).on("change", "#opcua-detail-nodeid-type", function () {
|
|
1942
|
+
var type = $(this).val();
|
|
1943
|
+
updateNodeIdValueInputState("detail", type);
|
|
1944
|
+
saveDetailNodeId(selectedPath);
|
|
1945
|
+
});
|
|
1946
|
+
$(document).on("input", "#opcua-detail-nodeid-value", function () {
|
|
1947
|
+
saveDetailNodeId(selectedPath);
|
|
1948
|
+
});
|
|
1354
1949
|
$(document).on("click", "#opcua-detail-copy-nodeid", function (event) {
|
|
1355
1950
|
event.preventDefault();
|
|
1356
1951
|
copyNodeIdValue(buildDisplayNodeIdFromEditorPath(selectedPath));
|
|
@@ -1389,6 +1984,7 @@
|
|
|
1389
1984
|
});
|
|
1390
1985
|
$(document).on("input", "#opcua-detail-variable-nodeid", function () { updateNode(selectedPath, { variableNodeId: $(this).val() }); });
|
|
1391
1986
|
$(document).on("input", "#opcua-detail-severity", function () { updateNode(selectedPath, { severity: Number($(this).val() || 0) }); });
|
|
1987
|
+
$(document).on("change", "#opcua-detail-sendvalue", function () { updateNode(selectedPath, { sendValue: $(this).is(":checked") }); });
|
|
1392
1988
|
$(document).on("input", "#opcua-detail-highhighlimit", function () { updateNode(selectedPath, { highHighLimit: Number($(this).val() || 0) }); });
|
|
1393
1989
|
$(document).on("input", "#opcua-detail-highhighmessage", function () { updateNode(selectedPath, { highHighMessage: $(this).val() }); });
|
|
1394
1990
|
$(document).on("input", "#opcua-detail-highlimit", function () { updateNode(selectedPath, { highLimit: Number($(this).val() || 0) }); });
|
|
@@ -1399,8 +1995,35 @@
|
|
|
1399
1995
|
$(document).on("input", "#opcua-detail-lowlowmessage", function () { updateNode(selectedPath, { lowLowMessage: $(this).val() }); });
|
|
1400
1996
|
$(document).on("input", "#opcua-detail-normalstatevalue", function () { updateNode(selectedPath, { normalStateValue: Number($(this).val() || 0) }); });
|
|
1401
1997
|
$(document).on("input", "#opcua-detail-digitalmessage", function () { updateNode(selectedPath, { digitalMessage: $(this).val() }); });
|
|
1402
|
-
$(document).on("input", "#opcua-create-name", function () {
|
|
1998
|
+
$(document).on("input", "#opcua-create-name", function () {
|
|
1999
|
+
if (pendingCreate) {
|
|
2000
|
+
var oldName = pendingCreate.name;
|
|
2001
|
+
var nextName = $(this).val();
|
|
2002
|
+
pendingCreate.name = nextName;
|
|
2003
|
+
|
|
2004
|
+
if (pendingCreate.kind === "variable" || pendingCreate.kind === "enum-variable") {
|
|
2005
|
+
if (pendingCreate.nodeIdType === "s") {
|
|
2006
|
+
var parentSuffix = buildDefaultNodeIdSuffixFromEditorPath(pendingCreate.parentPath);
|
|
2007
|
+
var oldAutoSuffix = parentSuffix ? parentSuffix + "." + oldName : oldName;
|
|
2008
|
+
var nextAutoSuffix = parentSuffix ? parentSuffix + "." + nextName : nextName;
|
|
2009
|
+
|
|
2010
|
+
if (!pendingCreate.nodeIdValue || pendingCreate.nodeIdValue === oldAutoSuffix) {
|
|
2011
|
+
pendingCreate.nodeIdValue = nextAutoSuffix;
|
|
2012
|
+
$("#opcua-create-nodeid-value").val(nextAutoSuffix);
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
2015
|
+
}
|
|
2016
|
+
}
|
|
2017
|
+
});
|
|
1403
2018
|
$(document).on("input", "#opcua-create-displayname", function () { if (pendingCreate) pendingCreate.displayName = $(this).val(); });
|
|
2019
|
+
$(document).on("change", "#opcua-create-nodeid-type", function () {
|
|
2020
|
+
var type = $(this).val();
|
|
2021
|
+
if (pendingCreate) pendingCreate.nodeIdType = type;
|
|
2022
|
+
updateNodeIdValueInputState("create", type);
|
|
2023
|
+
});
|
|
2024
|
+
$(document).on("input", "#opcua-create-nodeid-value", function () {
|
|
2025
|
+
if (pendingCreate) pendingCreate.nodeIdValue = $(this).val();
|
|
2026
|
+
});
|
|
1404
2027
|
$(document).on("change", "#opcua-create-accesspermission", function () { if (pendingCreate) pendingCreate.accessPermission = normalizeAccessPermissionValues($(this).val()); });
|
|
1405
2028
|
$(document).on("change", "#opcua-create-type", function () { if (pendingCreate) pendingCreate.dataType = $(this).val(); });
|
|
1406
2029
|
$(document).on("input", "#opcua-create-value", function () { if (pendingCreate) pendingCreate.value = $(this).val(); });
|
|
@@ -1414,6 +2037,7 @@
|
|
|
1414
2037
|
});
|
|
1415
2038
|
$(document).on("input", "#opcua-create-variable-nodeid", function () { if (pendingCreate) pendingCreate.variableNodeId = $(this).val(); });
|
|
1416
2039
|
$(document).on("input", "#opcua-create-severity", function () { if (pendingCreate) pendingCreate.severity = Number($(this).val() || 0); });
|
|
2040
|
+
$(document).on("change", "#opcua-create-sendvalue", function () { if (pendingCreate) pendingCreate.sendValue = $(this).is(":checked"); });
|
|
1417
2041
|
$(document).on("input", "#opcua-create-highhighlimit", function () { if (pendingCreate) pendingCreate.highHighLimit = Number($(this).val() || 0); });
|
|
1418
2042
|
$(document).on("input", "#opcua-create-highhighmessage", function () { if (pendingCreate) pendingCreate.highHighMessage = $(this).val(); });
|
|
1419
2043
|
$(document).on("input", "#opcua-create-highlimit", function () { if (pendingCreate) pendingCreate.highLimit = Number($(this).val() || 0); });
|
|
@@ -1475,9 +2099,12 @@
|
|
|
1475
2099
|
color: "#d9edf7",
|
|
1476
2100
|
credentials: { username: { type: "text" }, password: { type: "password" }, users: { type: "text" }, groups: { type: "text" } },
|
|
1477
2101
|
defaults: {
|
|
1478
|
-
name: { value: "" }, resourcePath: { value: "/" }, serverName: { value: "Node-RED OPC UA Server", required: true }, allowAnonymous: { value: true },
|
|
2102
|
+
name: { value: "" }, resourcePath: { value: "/" }, serverName: { value: "Node-RED OPC UA Server", required: true }, allowAnonymous: { value: true }, automaticallyAcceptUnknownCertificate: { value: true },
|
|
1479
2103
|
port: { value: 4840, required: true, validate: function (value) { var port = Number(value); return Number.isInteger(port) && port > 0 && port < 65536; } },
|
|
1480
2104
|
maxConnections: { value: 10, required: true, validate: function (value) { var n = Number(value); return Number.isInteger(n) && n > 0; } },
|
|
2105
|
+
minSessionTimeout: { value: 100, required: true, validate: function (value) { var n = Number(value); return Number.isInteger(n) && n >= 0; } },
|
|
2106
|
+
defaultSessionTimeout: { value: 30000, required: true, validate: function (value) { var n = Number(value); return Number.isInteger(n) && n >= 0; } },
|
|
2107
|
+
maxSessionTimeout: { value: 3000000, required: true, validate: function (value) { var n = Number(value); return Number.isInteger(n) && n >= 0; } },
|
|
1481
2108
|
securityPolicy: { value: "None", required: true }, securityMode: { value: "None", required: true }, namespaceUri: { value: "urn:node-red:opc-ua-server", required: true },
|
|
1482
2109
|
tree: {
|
|
1483
2110
|
value: "{\n \"folders\": [],\n \"objects\": [],\n \"objectsTypes\": [],\n \"nameSpaces\": [\n {\n \"id\": 2,\n \"name\": \"urn:node-red:opc-ua-server\"\n }\n ]\n}",
|
|
@@ -1493,6 +2120,15 @@
|
|
|
1493
2120
|
label: function () { return this.name || this.serverName || "opc-ua-server"; },
|
|
1494
2121
|
oneditprepare: function () {
|
|
1495
2122
|
var node = this;
|
|
2123
|
+
if (!$("#node-input-minSessionTimeout").val()) {
|
|
2124
|
+
$("#node-input-minSessionTimeout").val(100);
|
|
2125
|
+
}
|
|
2126
|
+
if (!$("#node-input-defaultSessionTimeout").val()) {
|
|
2127
|
+
$("#node-input-defaultSessionTimeout").val(30000);
|
|
2128
|
+
}
|
|
2129
|
+
if (!$("#node-input-maxSessionTimeout").val()) {
|
|
2130
|
+
$("#node-input-maxSessionTimeout").val(3000000);
|
|
2131
|
+
}
|
|
1496
2132
|
editorState = cloneTree(normalizeTree(parseTree(node.tree)));
|
|
1497
2133
|
authGroups = normalizeAuthGroups($("#node-input-groups").val());
|
|
1498
2134
|
authUsers = normalizeAuthUsers($("#node-input-users").val());
|
|
@@ -1501,6 +2137,35 @@
|
|
|
1501
2137
|
$("#node-input-namespaceUri").val(defaultNamespaceEntry.name);
|
|
1502
2138
|
}
|
|
1503
2139
|
syncAuthCredentialFields();
|
|
2140
|
+
$("#node-input-securityPolicy").typedInput({
|
|
2141
|
+
types: [
|
|
2142
|
+
{
|
|
2143
|
+
value: "securityPolicy",
|
|
2144
|
+
multiple: "true",
|
|
2145
|
+
options: [
|
|
2146
|
+
{ value: "None", label: "None" },
|
|
2147
|
+
{ value: "Basic128Rsa15", label: "Basic128Rsa15" },
|
|
2148
|
+
{ value: "Basic256", label: "Basic256" },
|
|
2149
|
+
{ value: "Basic256Sha256", label: "Basic256Sha256" },
|
|
2150
|
+
{ value: "Aes128_Sha256_RsaOaep", label: "Aes128_Sha256_RsaOaep" },
|
|
2151
|
+
{ value: "Aes256_Sha256_RsaPss", label: "Aes256_Sha256_RsaPss" }
|
|
2152
|
+
]
|
|
2153
|
+
}
|
|
2154
|
+
]
|
|
2155
|
+
});
|
|
2156
|
+
$("#node-input-securityMode").typedInput({
|
|
2157
|
+
types: [
|
|
2158
|
+
{
|
|
2159
|
+
value: "securityMode",
|
|
2160
|
+
multiple: "true",
|
|
2161
|
+
options: [
|
|
2162
|
+
{ value: "None", label: "None" },
|
|
2163
|
+
{ value: "Sign", label: "Sign" },
|
|
2164
|
+
{ value: "SignAndEncrypt", label: "SignAndEncrypt" }
|
|
2165
|
+
]
|
|
2166
|
+
}
|
|
2167
|
+
]
|
|
2168
|
+
});
|
|
1504
2169
|
updateTreeField(prettyTree(editorState), false);
|
|
1505
2170
|
$("#node-input-tree-editor").typedInput({ type: "json", types: ["json"] });
|
|
1506
2171
|
$("#node-input-tree-editor").typedInput("value", prettyTree(editorState));
|
|
@@ -1524,6 +2189,35 @@
|
|
|
1524
2189
|
$("#node-input-add-auth-group").off("click").on("click", function (event) { event.preventDefault(); addAuthGroup(); });
|
|
1525
2190
|
$("#node-input-add-auth-user").off("click").on("click", function (event) { event.preventDefault(); addAuthUser(); });
|
|
1526
2191
|
|
|
2192
|
+
$("#node-input-open-cert-modal").off("click").on("click", function (event) { event.preventDefault(); openCertModal(); });
|
|
2193
|
+
$("#node-input-close-cert-modal").off("click").on("click", function (event) { event.preventDefault(); closeCertModal(); });
|
|
2194
|
+
$("#node-input-cert-modal").off("click").on("click", function (event) { if (event.target === this) closeCertModal(); });
|
|
2195
|
+
|
|
2196
|
+
$("#node-input-open-settings-modal").off("click").on("click", function (event) { event.preventDefault(); openSettingsModal(); });
|
|
2197
|
+
$("#node-input-close-settings-modal").off("click").on("click", function (event) { event.preventDefault(); closeSettingsModal(); });
|
|
2198
|
+
$("#node-input-settings-modal").off("click").on("click", function (event) { if (event.target === this) closeSettingsModal(); });
|
|
2199
|
+
|
|
2200
|
+
$("#opcua-cert-folders").off("click", ".opcua-cert-item").on("click", ".opcua-cert-item", function () {
|
|
2201
|
+
$("#opcua-cert-folders .opcua-cert-item").removeClass("is-selected");
|
|
2202
|
+
$(this).addClass("is-selected");
|
|
2203
|
+
selectedCertFolder = $(this).attr("data-folder");
|
|
2204
|
+
selectedCertName = "";
|
|
2205
|
+
$("#opcua-cert-details").hide();
|
|
2206
|
+
renderCertificatesList();
|
|
2207
|
+
});
|
|
2208
|
+
|
|
2209
|
+
$("#opcua-cert-files").off("click", ".opcua-cert-item").on("click", ".opcua-cert-item", function () {
|
|
2210
|
+
$("#opcua-cert-files .opcua-cert-item").removeClass("is-selected");
|
|
2211
|
+
$(this).addClass("is-selected");
|
|
2212
|
+
selectedCertName = $(this).attr("data-name");
|
|
2213
|
+
showCertificateDetails();
|
|
2214
|
+
});
|
|
2215
|
+
|
|
2216
|
+
$("#opcua-cert-move-btn").off("click").on("click", function (event) {
|
|
2217
|
+
event.preventDefault();
|
|
2218
|
+
moveCertificate();
|
|
2219
|
+
});
|
|
2220
|
+
|
|
1527
2221
|
$("#node-input-tree-search").off("input").on("input", debounce(function () {
|
|
1528
2222
|
treeSearchValue = $(this).val(); treeSearchTerm = normalizeSearchTerm(treeSearchValue);
|
|
1529
2223
|
$("#node-input-tree-search-clear").toggle(!!treeSearchTerm); renderTree();
|
|
@@ -1546,6 +2240,10 @@
|
|
|
1546
2240
|
closeAuthModal();
|
|
1547
2241
|
return;
|
|
1548
2242
|
}
|
|
2243
|
+
if ($("#node-input-cert-modal").is(":visible")) {
|
|
2244
|
+
closeCertModal();
|
|
2245
|
+
return;
|
|
2246
|
+
}
|
|
1549
2247
|
closeTreeModal();
|
|
1550
2248
|
});
|
|
1551
2249
|
|