@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.
- package/README.md +89 -136
- package/client/lib/opcua-client-browser.js +238 -10
- package/client/lib/opcua-client-method-service.js +1 -1
- package/client/lib/opcua-client-subscription-service.js +0 -2
- package/client/opcua-client-config.html +118 -1
- package/client/opcua-client-config.js +74 -8
- package/client/opcua-client-help.html +6 -0
- package/client/opcua-client-utils.js +34 -10
- package/client/opcua-client.html +7 -0
- package/client/opcua-client.js +97 -1
- package/examples/flows_simple_opc.json +1 -1
- package/package.json +1 -1
- package/server/lib/opcua-address-space-alarm.js +11 -5
- package/server/lib/opcua-address-space-builder.js +65 -15
- package/server/lib/opcua-config.js +81 -23
- package/server/lib/opcua-server-events-child.js +1 -1
- package/server/lib/opcua-server-runtime-child.js +429 -59
- package/server/lib/opcua-server-runtime.js +49 -5
- package/server/lib/opcua-server-status-child.js +14 -14
- 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 +76 -0
- package/server/opcua-server-io.js +135 -23
- package/server/opcua-server.css +52 -0
- package/server/opcua-server.html +166 -44
- package/server/opcua-server.js +142 -7
- package/server/view/opcua-server.css +89 -6
- package/server/view/opcua-server.js +523 -42
|
@@ -12,13 +12,136 @@
|
|
|
12
12
|
var isSyncing = false;
|
|
13
13
|
var DEFAULT_NAMESPACE_ID = 2;
|
|
14
14
|
|
|
15
|
+
var selectedCertFolder = "rejected";
|
|
16
|
+
var selectedCertName = "";
|
|
17
|
+
var certificatesData = { trusted: [], rejected: [] };
|
|
18
|
+
|
|
15
19
|
function syncModalBodyClass() {
|
|
16
|
-
$("body").toggleClass("opcua-tree-modal-open",
|
|
20
|
+
$("body").toggleClass("opcua-tree-modal-open",
|
|
21
|
+
$("#node-input-tree-modal").is(":visible") ||
|
|
22
|
+
$("#node-input-auth-modal").is(":visible") ||
|
|
23
|
+
$("#node-input-cert-modal").is(":visible") ||
|
|
24
|
+
$("#node-input-settings-modal").is(":visible")
|
|
25
|
+
);
|
|
17
26
|
}
|
|
18
27
|
function openTreeModal() { $("#node-input-tree-modal").show(); syncModalBodyClass(); }
|
|
19
28
|
function closeTreeModal() { $("#node-input-tree-modal").hide(); syncModalBodyClass(); }
|
|
20
29
|
function openAuthModal() { $("#node-input-auth-modal").show(); syncModalBodyClass(); renderAuthEditor(); }
|
|
21
30
|
function closeAuthModal() { $("#node-input-auth-modal").hide(); syncModalBodyClass(); }
|
|
31
|
+
function openCertModal() { $("#node-input-cert-modal").show(); syncModalBodyClass(); initCertEditor(); }
|
|
32
|
+
function closeCertModal() { $("#node-input-cert-modal").hide(); syncModalBodyClass(); }
|
|
33
|
+
function openSettingsModal() { $("#node-input-settings-modal").show(); syncModalBodyClass(); }
|
|
34
|
+
function closeSettingsModal() { $("#node-input-settings-modal").hide(); syncModalBodyClass(); }
|
|
35
|
+
|
|
36
|
+
function initCertEditor() {
|
|
37
|
+
selectedCertFolder = "rejected";
|
|
38
|
+
selectedCertName = "";
|
|
39
|
+
$("#opcua-cert-details").hide();
|
|
40
|
+
$("#opcua-cert-folders .opcua-cert-item").removeClass("is-selected");
|
|
41
|
+
$('#opcua-cert-folders .opcua-cert-item[data-folder="rejected"]').addClass("is-selected");
|
|
42
|
+
fetchCertificates();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function fetchCertificates() {
|
|
46
|
+
var filesContainer = $("#opcua-cert-files");
|
|
47
|
+
filesContainer.empty().append('<div class="opcua-tree-empty"><i class="fa fa-spinner fa-spin"></i> Loading certificates...</div>');
|
|
48
|
+
|
|
49
|
+
var currentServerName = $("#node-input-serverName").val() || "";
|
|
50
|
+
|
|
51
|
+
$.ajax({
|
|
52
|
+
url: "opc-ua-server/certificates",
|
|
53
|
+
type: "GET",
|
|
54
|
+
data: {
|
|
55
|
+
serverName: currentServerName
|
|
56
|
+
},
|
|
57
|
+
dataType: "json",
|
|
58
|
+
success: function (data) {
|
|
59
|
+
certificatesData = data || { trusted: [], rejected: [] };
|
|
60
|
+
renderCertificatesList();
|
|
61
|
+
},
|
|
62
|
+
error: function (xhr, textStatus, errorThrown) {
|
|
63
|
+
filesContainer.empty().append('<div class="opcua-tree-empty" style="color: #d9534f;"><i class="fa fa-exclamation-triangle"></i> Failed to load certificates.</div>');
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function renderCertificatesList() {
|
|
69
|
+
var filesContainer = $("#opcua-cert-files");
|
|
70
|
+
filesContainer.empty();
|
|
71
|
+
|
|
72
|
+
var list = certificatesData[selectedCertFolder] || [];
|
|
73
|
+
if (list.length === 0) {
|
|
74
|
+
filesContainer.append('<div class="opcua-tree-empty">No certificates found in this folder.</div>');
|
|
75
|
+
$("#opcua-cert-details").hide();
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
list.forEach(function (filename) {
|
|
80
|
+
var item = $('<div class="opcua-cert-item"></div>');
|
|
81
|
+
item.attr("data-name", filename);
|
|
82
|
+
item.append('<i class="fa fa-certificate"></i> ' + escapeHtml(filename));
|
|
83
|
+
if (filename === selectedCertName) {
|
|
84
|
+
item.addClass("is-selected");
|
|
85
|
+
}
|
|
86
|
+
filesContainer.append(item);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// If previously selected cert is not in the list, hide details
|
|
90
|
+
if (selectedCertName && list.indexOf(selectedCertName) === -1) {
|
|
91
|
+
selectedCertName = "";
|
|
92
|
+
$("#opcua-cert-details").hide();
|
|
93
|
+
} else if (selectedCertName) {
|
|
94
|
+
showCertificateDetails();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function showCertificateDetails() {
|
|
99
|
+
$("#opcua-selected-cert-name").text(selectedCertName);
|
|
100
|
+
var targetSelect = $("#opcua-cert-target-folder");
|
|
101
|
+
targetSelect.empty();
|
|
102
|
+
|
|
103
|
+
if (selectedCertFolder === "rejected") {
|
|
104
|
+
targetSelect.append('<option value="trusted">Trusted Certificates</option>');
|
|
105
|
+
} else {
|
|
106
|
+
targetSelect.append('<option value="rejected">Rejected Certificates</option>');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
$("#opcua-cert-details").show();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function moveCertificate() {
|
|
113
|
+
if (!selectedCertName) return;
|
|
114
|
+
var targetFolder = $("#opcua-cert-target-folder").val();
|
|
115
|
+
if (!targetFolder) return;
|
|
116
|
+
|
|
117
|
+
var currentServerName = $("#node-input-serverName").val() || "";
|
|
118
|
+
var moveBtn = $("#opcua-cert-move-btn");
|
|
119
|
+
moveBtn.addClass("disabled").append(' <i class="fa fa-spinner fa-spin"></i>');
|
|
120
|
+
|
|
121
|
+
$.ajax({
|
|
122
|
+
url: "opc-ua-server/certificates/move",
|
|
123
|
+
type: "POST",
|
|
124
|
+
contentType: "application/json",
|
|
125
|
+
data: JSON.stringify({
|
|
126
|
+
serverName: currentServerName,
|
|
127
|
+
filename: selectedCertName,
|
|
128
|
+
fromFolder: selectedCertFolder,
|
|
129
|
+
toFolder: targetFolder
|
|
130
|
+
}),
|
|
131
|
+
success: function (res) {
|
|
132
|
+
moveBtn.removeClass("disabled").find("i.fa-spin").remove();
|
|
133
|
+
selectedCertName = "";
|
|
134
|
+
$("#opcua-cert-details").hide();
|
|
135
|
+
RED.notify("Certificate moved successfully.", "success");
|
|
136
|
+
fetchCertificates();
|
|
137
|
+
},
|
|
138
|
+
error: function (xhr, textStatus, errorThrown) {
|
|
139
|
+
moveBtn.removeClass("disabled").find("i.fa-spin").remove();
|
|
140
|
+
var errMsg = xhr.responseJSON && xhr.responseJSON.error ? xhr.responseJSON.error : "Failed to move certificate.";
|
|
141
|
+
RED.notify(errMsg, "error");
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
}
|
|
22
145
|
|
|
23
146
|
function parseTree(rawValue, strict) {
|
|
24
147
|
if (!rawValue) return { objects: [], folders: [], objectsTypes: [], enumerations: [], nameSpaces: [] };
|
|
@@ -174,19 +297,20 @@
|
|
|
174
297
|
variableNodeId: alarm.variableNodeId ? String(alarm.variableNodeId) : "",
|
|
175
298
|
type: type,
|
|
176
299
|
enabled: alarm.enabled !== undefined ? !!alarm.enabled : true,
|
|
300
|
+
sendValue: alarm.sendValue !== undefined ? !!alarm.sendValue : true,
|
|
177
301
|
severity: alarm.severity !== undefined ? alarm.severity : 500,
|
|
178
302
|
description: alarm.description ? String(alarm.description) : "",
|
|
179
303
|
displayName: alarm.displayName ? String(alarm.displayName) : "",
|
|
180
304
|
nodeId: alarm.nodeId ? String(alarm.nodeId) : "",
|
|
181
305
|
namespaceId: normalizeNamespaceId(alarm.namespaceId),
|
|
182
306
|
accessPermission: normalizeAccessPermissionValues(alarm.accessPermission || alarm.accessPermissions),
|
|
183
|
-
highHighLimit: alarm.highHighLimit !== undefined ? alarm.highHighLimit :
|
|
307
|
+
highHighLimit: alarm.highHighLimit !== undefined ? alarm.highHighLimit : 90,
|
|
184
308
|
highHighMessage: alarm.highHighMessage ? String(alarm.highHighMessage) : "High High alarm",
|
|
185
309
|
highLimit: alarm.highLimit !== undefined ? alarm.highLimit : 80,
|
|
186
310
|
highMessage: alarm.highMessage ? String(alarm.highMessage) : "High alarm",
|
|
187
311
|
lowLimit: alarm.lowLimit !== undefined ? alarm.lowLimit : 20,
|
|
188
312
|
lowMessage: alarm.lowMessage ? String(alarm.lowMessage) : "Low alarm",
|
|
189
|
-
lowLowLimit: alarm.lowLowLimit !== undefined ? alarm.lowLowLimit :
|
|
313
|
+
lowLowLimit: alarm.lowLowLimit !== undefined ? alarm.lowLowLimit : 10,
|
|
190
314
|
lowLowMessage: alarm.lowLowMessage ? String(alarm.lowLowMessage) : "Low Low alarm",
|
|
191
315
|
normalStateValue: alarm.normalStateValue !== undefined ? alarm.normalStateValue : 0,
|
|
192
316
|
digitalMessage: alarm.digitalMessage ? String(alarm.digitalMessage) : "Digital alarm"
|
|
@@ -320,6 +444,7 @@
|
|
|
320
444
|
function normalizeSearchTerm(value) { return String(value || "").trim().toLowerCase(); }
|
|
321
445
|
function isExpanded(path, defaultValue) { if (expansionState[path] === undefined) expansionState[path] = !!defaultValue; return expansionState[path]; }
|
|
322
446
|
function nodeClassFromPath(path) {
|
|
447
|
+
if (path && path.indexOf("virtual:") === 0) return "VisualFolder";
|
|
323
448
|
var tokens = pathToTokens(path);
|
|
324
449
|
if (!tokens.length) return "Object";
|
|
325
450
|
|
|
@@ -333,7 +458,18 @@
|
|
|
333
458
|
if (collectionToken === "folders") return "Folder";
|
|
334
459
|
return "Object";
|
|
335
460
|
}
|
|
336
|
-
function
|
|
461
|
+
function getVirtualNodeName(path) {
|
|
462
|
+
if (path === "virtual:Objects") return "Objects";
|
|
463
|
+
if (path === "virtual:Types") return "Types";
|
|
464
|
+
if (path === "virtual:Types.ObjectTypes") return "ObjectTypes";
|
|
465
|
+
if (path === "virtual:Types.DataTypes") return "DataTypes";
|
|
466
|
+
return "";
|
|
467
|
+
}
|
|
468
|
+
function getNodeDisplayName(path) {
|
|
469
|
+
if (path && path.indexOf("virtual:") === 0) return getVirtualNodeName(path);
|
|
470
|
+
var item = getAtPath(editorState, path);
|
|
471
|
+
return item ? (item.name || "(unnamed)") : "";
|
|
472
|
+
}
|
|
337
473
|
function getNamespaceOptions() {
|
|
338
474
|
return Array.isArray(editorState.nameSpaces) ? editorState.nameSpaces.slice().sort(function (left, right) { return left.id - right.id; }) : [];
|
|
339
475
|
}
|
|
@@ -444,7 +580,7 @@
|
|
|
444
580
|
function nodeIdSuffixFromValue(nodeId, defaultSuffix) {
|
|
445
581
|
var raw = String(nodeId || "").trim();
|
|
446
582
|
if (!raw) return defaultSuffix;
|
|
447
|
-
var match = /^ns=\d+;
|
|
583
|
+
var match = /^ns=\d+;[si]=(.*)$/.exec(raw);
|
|
448
584
|
if (match) return match[1];
|
|
449
585
|
return raw;
|
|
450
586
|
}
|
|
@@ -485,8 +621,69 @@
|
|
|
485
621
|
var item = getAtPath(editorState, path);
|
|
486
622
|
if (isObjectTypeModelPath(path)) return buildGeneratedNodeIdForPath(path);
|
|
487
623
|
var customNodeId = item && item.nodeId ? String(item.nodeId).trim() : "";
|
|
488
|
-
|
|
489
|
-
|
|
624
|
+
if (customNodeId) {
|
|
625
|
+
if (/^ns=\d+;[si]=/.test(customNodeId)) {
|
|
626
|
+
return customNodeId;
|
|
627
|
+
}
|
|
628
|
+
return getNodeIdPrefix(getNodeNamespaceId(path)) + customNodeId;
|
|
629
|
+
}
|
|
630
|
+
return buildGeneratedNodeIdForPath(path);
|
|
631
|
+
}
|
|
632
|
+
function parseNodeId(nodeId, defaultSuffix) {
|
|
633
|
+
var raw = String(nodeId || "").trim();
|
|
634
|
+
if (!raw) {
|
|
635
|
+
return { type: "s", value: defaultSuffix };
|
|
636
|
+
}
|
|
637
|
+
var match = /^ns=\d+;([si])=(.*)$/.exec(raw);
|
|
638
|
+
if (match) {
|
|
639
|
+
return { type: match[1], value: match[2] };
|
|
640
|
+
}
|
|
641
|
+
return { type: "s", value: raw };
|
|
642
|
+
}
|
|
643
|
+
function updateNodeIdValueInputState(mode, type) {
|
|
644
|
+
var inputId = "#opcua-" + mode + "-nodeid-value";
|
|
645
|
+
var labelId = "#opcua-" + mode + "-nodeid-value-label";
|
|
646
|
+
var input = $(inputId);
|
|
647
|
+
var label = $(labelId);
|
|
648
|
+
|
|
649
|
+
if (type === "i") {
|
|
650
|
+
if (label.length) label.text("nodeId Value (Numeric)");
|
|
651
|
+
input.attr("type", "number");
|
|
652
|
+
input.attr("step", "1");
|
|
653
|
+
input.attr("placeholder", "Enter a number");
|
|
654
|
+
} else {
|
|
655
|
+
if (label.length) label.text("nodeId Value (String)");
|
|
656
|
+
input.attr("type", "text");
|
|
657
|
+
input.removeAttr("step");
|
|
658
|
+
if (mode === "create") {
|
|
659
|
+
input.attr("placeholder", "Leave blank for default (s)");
|
|
660
|
+
} else {
|
|
661
|
+
input.attr("placeholder", "Enter a string");
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
function saveDetailNodeId(path) {
|
|
666
|
+
if (!path) return;
|
|
667
|
+
var type = $("#opcua-detail-nodeid-type").val();
|
|
668
|
+
var rawVal = $("#opcua-detail-nodeid-value").val();
|
|
669
|
+
var nsId = getNodeNamespaceId(path);
|
|
670
|
+
|
|
671
|
+
var customNodeId = "";
|
|
672
|
+
if (rawVal) {
|
|
673
|
+
if (type === "i") {
|
|
674
|
+
var numVal = parseInt(rawVal, 10);
|
|
675
|
+
if (!isNaN(numVal)) {
|
|
676
|
+
customNodeId = "ns=" + nsId + ";i=" + numVal;
|
|
677
|
+
}
|
|
678
|
+
} else {
|
|
679
|
+
var defaultSuffix = buildDefaultNodeIdSuffixFromEditorPath(path);
|
|
680
|
+
var nextSuffix = String(rawVal).trim();
|
|
681
|
+
if (nextSuffix && nextSuffix !== defaultSuffix) {
|
|
682
|
+
customNodeId = "ns=" + nsId + ";s=" + nextSuffix;
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
updateNode(path, { nodeId: customNodeId });
|
|
490
687
|
}
|
|
491
688
|
function normalizeCustomNodeIdFromSuffix(path, suffix) {
|
|
492
689
|
if (isObjectTypeModelPath(path)) return "";
|
|
@@ -527,6 +724,25 @@
|
|
|
527
724
|
}
|
|
528
725
|
|
|
529
726
|
function getChildrenByPath(path) {
|
|
727
|
+
if (path === "virtual:Objects") {
|
|
728
|
+
var children = [];
|
|
729
|
+
(editorState.folders || []).forEach(function (_, i) { children.push("folders." + i); });
|
|
730
|
+
(editorState.objects || []).forEach(function (_, i) { children.push("objects." + i); });
|
|
731
|
+
return children;
|
|
732
|
+
}
|
|
733
|
+
if (path === "virtual:Types") {
|
|
734
|
+
return ["virtual:Types.ObjectTypes", "virtual:Types.DataTypes"];
|
|
735
|
+
}
|
|
736
|
+
if (path === "virtual:Types.ObjectTypes") {
|
|
737
|
+
var children = [];
|
|
738
|
+
(editorState.objectsTypes || []).forEach(function (_, i) { children.push("objectsTypes." + i); });
|
|
739
|
+
return children;
|
|
740
|
+
}
|
|
741
|
+
if (path === "virtual:Types.DataTypes") {
|
|
742
|
+
var children = [];
|
|
743
|
+
(editorState.enumerations || []).forEach(function (_, i) { children.push("enumerations." + i); });
|
|
744
|
+
return children;
|
|
745
|
+
}
|
|
530
746
|
var item = getAtPath(editorState, path);
|
|
531
747
|
if (!item) return [];
|
|
532
748
|
var children = [];
|
|
@@ -540,11 +756,7 @@
|
|
|
540
756
|
}
|
|
541
757
|
|
|
542
758
|
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); });
|
|
759
|
+
var paths = ["virtual:Objects", "virtual:Types"];
|
|
548
760
|
(editorState.nameSpaces || []).forEach(function (_, i) { paths.push("nameSpaces." + i); });
|
|
549
761
|
return paths;
|
|
550
762
|
}
|
|
@@ -556,6 +768,9 @@
|
|
|
556
768
|
|
|
557
769
|
function nodeMatchesSearch(path) {
|
|
558
770
|
if (!treeSearchTerm) return true;
|
|
771
|
+
if (path && path.indexOf("virtual:") === 0) {
|
|
772
|
+
return getVirtualNodeName(path).toLowerCase().indexOf(treeSearchTerm) !== -1;
|
|
773
|
+
}
|
|
559
774
|
var item = getAtPath(editorState, path);
|
|
560
775
|
if (!item) return false;
|
|
561
776
|
var values = [path, item.name, item.displayName, item.description, nodeClassFromPath(path), item.type, item.value, item.id, item.namespaceId];
|
|
@@ -568,7 +783,7 @@
|
|
|
568
783
|
}
|
|
569
784
|
|
|
570
785
|
function iconForNodeClass(nodeClass) {
|
|
571
|
-
if (nodeClass === "Folder") return "fa-folder";
|
|
786
|
+
if (nodeClass === "Folder" || nodeClass === "VisualFolder") return "fa-folder";
|
|
572
787
|
if (nodeClass === "Object") return "fa-cube";
|
|
573
788
|
if (nodeClass === "Variable") return "fa-tag";
|
|
574
789
|
if (nodeClass === "ObjectType") return "fa-cubes";
|
|
@@ -594,7 +809,7 @@
|
|
|
594
809
|
}
|
|
595
810
|
|
|
596
811
|
function appendNodeToFrag(frag, path, depth, ancestorMatched) {
|
|
597
|
-
var item = getAtPath(editorState, path);
|
|
812
|
+
var item = (path && path.indexOf("virtual:") === 0) ? {} : getAtPath(editorState, path);
|
|
598
813
|
if (!item) return;
|
|
599
814
|
var nodeClass = nodeClassFromPath(path);
|
|
600
815
|
var hasChildren = nodeClass !== "Variable" && nodeClass !== "Alarm" && nodeClass !== "Namespace" && getChildrenByPath(path).length > 0;
|
|
@@ -607,11 +822,13 @@
|
|
|
607
822
|
var row = document.createElement("div");
|
|
608
823
|
row.className = "opcua-tree-row" + (path === selectedPath ? " is-selected" : "");
|
|
609
824
|
row.setAttribute("data-path", path);
|
|
825
|
+
var label = (path && path.indexOf("virtual:") === 0) ? getVirtualNodeName(path) : (item.name || "(unnamed)");
|
|
826
|
+
var displayClass = (path && path.indexOf("virtual:") === 0) ? "Folder" : nodeClass;
|
|
610
827
|
row.innerHTML = indents
|
|
611
828
|
+ '<span class="opcua-tree-twisty">' + (hasChildren ? '<i class="fa ' + (expanded ? "fa-caret-down" : "fa-caret-right") + '"></i>' : "") + "</span>"
|
|
612
829
|
+ '<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(
|
|
830
|
+
+ '<span class="opcua-tree-label">' + escapeHtml(label) + "</span>"
|
|
831
|
+
+ '<span class="opcua-tree-type">' + escapeHtml(displayClass) + "</span>";
|
|
615
832
|
frag.appendChild(row);
|
|
616
833
|
|
|
617
834
|
if (hasChildren && expanded) {
|
|
@@ -650,9 +867,28 @@
|
|
|
650
867
|
function renderBreadcrumbs() {
|
|
651
868
|
var el = $("#opcua-tree-breadcrumbs");
|
|
652
869
|
if (!selectedPath) { el.text("No selection"); return; }
|
|
870
|
+
if (selectedPath.indexOf("virtual:") === 0) {
|
|
871
|
+
var parts = [];
|
|
872
|
+
if (selectedPath === "virtual:Objects") parts = ["Objects"];
|
|
873
|
+
else if (selectedPath === "virtual:Types") parts = ["Types"];
|
|
874
|
+
else if (selectedPath === "virtual:Types.ObjectTypes") parts = ["Types", "ObjectTypes"];
|
|
875
|
+
else if (selectedPath === "virtual:Types.DataTypes") parts = ["Types", "DataTypes"];
|
|
876
|
+
el.text(parts.join("."));
|
|
877
|
+
return;
|
|
878
|
+
}
|
|
653
879
|
var tokens = pathToTokens(selectedPath);
|
|
654
880
|
var cursor = [];
|
|
655
881
|
var parts = [];
|
|
882
|
+
var rootToken = tokens[0];
|
|
883
|
+
if (rootToken === "folders" || rootToken === "objects") {
|
|
884
|
+
parts.push("Objects");
|
|
885
|
+
} else if (rootToken === "objectsTypes") {
|
|
886
|
+
parts.push("Types");
|
|
887
|
+
parts.push("ObjectTypes");
|
|
888
|
+
} else if (rootToken === "enumerations") {
|
|
889
|
+
parts.push("Types");
|
|
890
|
+
parts.push("DataTypes");
|
|
891
|
+
}
|
|
656
892
|
tokens.forEach(function (token) {
|
|
657
893
|
cursor.push(token);
|
|
658
894
|
if (/^\d+$/.test(token)) parts.push(getNodeDisplayName(cursor.join(".")) || ("#" + token));
|
|
@@ -672,14 +908,38 @@
|
|
|
672
908
|
|
|
673
909
|
function openCreateForm(path, kind) {
|
|
674
910
|
if (!path) return;
|
|
675
|
-
if (
|
|
911
|
+
if (path.indexOf("virtual:") === 0) {
|
|
912
|
+
if (path === "virtual:Objects") {
|
|
913
|
+
if (kind !== "folder" && kind !== "object") {
|
|
914
|
+
RED.notify("Only Folders and Objects can be added directly under Objects", "warning");
|
|
915
|
+
return;
|
|
916
|
+
}
|
|
917
|
+
} else if (path === "virtual:Types.ObjectTypes") {
|
|
918
|
+
if (kind !== "objecttype") {
|
|
919
|
+
RED.notify("Only ObjectTypes can be added under ObjectTypes", "warning");
|
|
920
|
+
return;
|
|
921
|
+
}
|
|
922
|
+
} else if (path === "virtual:Types.DataTypes") {
|
|
923
|
+
if (kind !== "enumeration") {
|
|
924
|
+
RED.notify("Only Enumerations can be added under DataTypes", "warning");
|
|
925
|
+
return;
|
|
926
|
+
}
|
|
927
|
+
} else {
|
|
928
|
+
RED.notify("Cannot add children to this visual folder", "warning");
|
|
929
|
+
return;
|
|
930
|
+
}
|
|
931
|
+
} else if (nodeClassFromPath(path) === "Variable" || nodeClassFromPath(path) === "Namespace") {
|
|
676
932
|
RED.notify("Selected item cannot have children", "warning");
|
|
677
933
|
return;
|
|
678
934
|
}
|
|
935
|
+
var parentSuffix = path.indexOf("virtual:") === 0 ? "" : buildDefaultNodeIdSuffixFromEditorPath(path);
|
|
936
|
+
var defaultName = kind === "variable" ? "newVariable" : kind === "enum-variable" ? "newEnumVariable" : "newObject";
|
|
937
|
+
var defaultSuffix = parentSuffix ? parentSuffix + "." + defaultName : defaultName;
|
|
938
|
+
|
|
679
939
|
pendingCreate = {
|
|
680
940
|
parentPath: path,
|
|
681
941
|
kind: kind,
|
|
682
|
-
name:
|
|
942
|
+
name: defaultName,
|
|
683
943
|
displayName: "",
|
|
684
944
|
dataType: kind === "enum-variable" ? (getDefinedEnumerationNames()[0] || "") : "Int32",
|
|
685
945
|
value: "",
|
|
@@ -689,16 +949,19 @@
|
|
|
689
949
|
alarmType: "levelAlarm",
|
|
690
950
|
variableNodeId: "",
|
|
691
951
|
severity: 500,
|
|
692
|
-
|
|
952
|
+
sendValue: true,
|
|
953
|
+
highHighLimit: 90,
|
|
693
954
|
highHighMessage: "High High alarm",
|
|
694
955
|
highLimit: 80,
|
|
695
956
|
highMessage: "High alarm",
|
|
696
957
|
lowLimit: 20,
|
|
697
958
|
lowMessage: "Low alarm",
|
|
698
|
-
lowLowLimit:
|
|
959
|
+
lowLowLimit: 10,
|
|
699
960
|
lowLowMessage: "Low Low alarm",
|
|
700
961
|
normalStateValue: 0,
|
|
701
|
-
digitalMessage: "Digital alarm"
|
|
962
|
+
digitalMessage: "Digital alarm",
|
|
963
|
+
nodeIdType: "s",
|
|
964
|
+
nodeIdValue: (kind === "variable" || kind === "enum-variable") ? defaultSuffix : ""
|
|
702
965
|
};
|
|
703
966
|
renderDetails();
|
|
704
967
|
}
|
|
@@ -707,27 +970,51 @@
|
|
|
707
970
|
if (!pendingCreate) return;
|
|
708
971
|
var parentPath = pendingCreate.parentPath;
|
|
709
972
|
var kind = pendingCreate.kind;
|
|
710
|
-
var branchTargetPath
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
973
|
+
var branchTargetPath;
|
|
974
|
+
if (parentPath.indexOf("virtual:") === 0) {
|
|
975
|
+
if (parentPath === "virtual:Objects") {
|
|
976
|
+
branchTargetPath = kind === "folder" ? "folders" : "objects";
|
|
977
|
+
} else if (parentPath === "virtual:Types.ObjectTypes") {
|
|
978
|
+
branchTargetPath = "objectsTypes";
|
|
979
|
+
} else {
|
|
980
|
+
return;
|
|
981
|
+
}
|
|
982
|
+
} else {
|
|
983
|
+
branchTargetPath = (kind === "variable" || kind === "enum-variable")
|
|
984
|
+
? (parentPath + ".variables")
|
|
985
|
+
: kind === "folder"
|
|
986
|
+
? (parentPath + ".folders")
|
|
987
|
+
: kind === "objecttype"
|
|
988
|
+
? (parentPath + ".objectsTypes")
|
|
989
|
+
: kind === "alarm"
|
|
990
|
+
? (parentPath + ".alarms")
|
|
991
|
+
: kind === "method"
|
|
992
|
+
? (parentPath + ".methods")
|
|
993
|
+
: (parentPath + ".objects");
|
|
994
|
+
}
|
|
721
995
|
var target = getAtPath(editorState, branchTargetPath);
|
|
722
996
|
if (!Array.isArray(target)) return;
|
|
723
997
|
if (kind === "variable" || kind === "enum-variable") {
|
|
998
|
+
var customNodeId = "";
|
|
999
|
+
var nsId = getNodeNamespaceId(parentPath);
|
|
1000
|
+
if (pendingCreate.nodeIdValue) {
|
|
1001
|
+
if (pendingCreate.nodeIdType === "i") {
|
|
1002
|
+
var numVal = parseInt(pendingCreate.nodeIdValue, 10);
|
|
1003
|
+
if (!isNaN(numVal)) {
|
|
1004
|
+
customNodeId = "ns=" + nsId + ";i=" + numVal;
|
|
1005
|
+
}
|
|
1006
|
+
} else {
|
|
1007
|
+
customNodeId = "ns=" + nsId + ";s=" + pendingCreate.nodeIdValue.trim();
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
724
1010
|
target.push(normalizeVariable({
|
|
725
1011
|
name: pendingCreate.name,
|
|
726
1012
|
displayName: pendingCreate.displayName || "",
|
|
727
1013
|
type: pendingCreate.dataType,
|
|
728
1014
|
value: pendingCreate.value,
|
|
729
1015
|
access: pendingCreate.access || "readwrite",
|
|
730
|
-
accessPermission: pendingCreate.accessPermission
|
|
1016
|
+
accessPermission: pendingCreate.accessPermission,
|
|
1017
|
+
nodeId: customNodeId
|
|
731
1018
|
}));
|
|
732
1019
|
} else if (kind === "folder") {
|
|
733
1020
|
target.push(normalizeBranch({ name: pendingCreate.name, displayName: pendingCreate.displayName || "", accessPermission: pendingCreate.accessPermission }));
|
|
@@ -742,6 +1029,7 @@
|
|
|
742
1029
|
type: pendingCreate.alarmType,
|
|
743
1030
|
variableNodeId: pendingCreate.variableNodeId,
|
|
744
1031
|
severity: Number(pendingCreate.severity || 500),
|
|
1032
|
+
sendValue: pendingCreate.sendValue,
|
|
745
1033
|
highHighLimit: pendingCreate.highHighLimit,
|
|
746
1034
|
highHighMessage: pendingCreate.highHighMessage,
|
|
747
1035
|
highLimit: pendingCreate.highLimit,
|
|
@@ -784,6 +1072,8 @@
|
|
|
784
1072
|
panel.append('<div class="form-row"><label>dataType</label>' + typeHtml + '</div>');
|
|
785
1073
|
panel.append('<div class="form-row"><label>Value</label><input type="text" id="opcua-create-value"></div>');
|
|
786
1074
|
panel.append('<div class="form-row"><label>Access</label><select id="opcua-create-access"><option value="readwrite">readwrite</option><option value="readonly">readonly</option></select></div>');
|
|
1075
|
+
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>');
|
|
1076
|
+
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>');
|
|
787
1077
|
}
|
|
788
1078
|
if (pendingCreate.kind === "objecttype") {
|
|
789
1079
|
panel.append('<div class="form-row"><label>objectsType</label>' + buildObjectTypeSelect("opcua-create-objectstype", pendingCreate.objectsType || "") + '</div>');
|
|
@@ -792,6 +1082,7 @@
|
|
|
792
1082
|
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
1083
|
panel.append('<div class="form-row"><label>variablePath</label><input type="text" id="opcua-create-variable-nodeid"></div>');
|
|
794
1084
|
panel.append('<div class="form-row"><label>severity</label><input type="number" id="opcua-create-severity"></div>');
|
|
1085
|
+
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
1086
|
if (pendingCreate.alarmType === "levelAlarm") {
|
|
796
1087
|
panel.append('<div class="form-row"><label>highHighLimit</label><input type="number" id="opcua-create-highhighlimit"></div>');
|
|
797
1088
|
panel.append('<div class="form-row"><label>highHighMessage</label><input type="text" id="opcua-create-highhighmessage"></div>');
|
|
@@ -812,6 +1103,11 @@
|
|
|
812
1103
|
$("#opcua-create-name").val(pendingCreate.name);
|
|
813
1104
|
$("#opcua-create-displayname").val(pendingCreate.displayName || "");
|
|
814
1105
|
$("#opcua-create-accesspermission").val(normalizeAccessPermissionValues(pendingCreate.accessPermission));
|
|
1106
|
+
if (pendingCreate.kind === "variable" || pendingCreate.kind === "enum-variable") {
|
|
1107
|
+
$("#opcua-create-nodeid-type").val(pendingCreate.nodeIdType || "s");
|
|
1108
|
+
$("#opcua-create-nodeid-value").val(pendingCreate.nodeIdValue || "");
|
|
1109
|
+
updateNodeIdValueInputState("create", pendingCreate.nodeIdType || "s");
|
|
1110
|
+
}
|
|
815
1111
|
$("#opcua-create-type").val(pendingCreate.dataType);
|
|
816
1112
|
$("#opcua-create-value").val(pendingCreate.value);
|
|
817
1113
|
$("#opcua-create-objectstype").val(pendingCreate.objectsType);
|
|
@@ -819,6 +1115,7 @@
|
|
|
819
1115
|
$("#opcua-create-alarm-type").val(pendingCreate.alarmType);
|
|
820
1116
|
$("#opcua-create-variable-nodeid").val(pendingCreate.variableNodeId);
|
|
821
1117
|
$("#opcua-create-severity").val(pendingCreate.severity);
|
|
1118
|
+
$("#opcua-create-sendvalue").prop("checked", pendingCreate.sendValue !== false);
|
|
822
1119
|
$("#opcua-create-highhighlimit").val(pendingCreate.highHighLimit);
|
|
823
1120
|
$("#opcua-create-highhighmessage").val(pendingCreate.highHighMessage);
|
|
824
1121
|
$("#opcua-create-highlimit").val(pendingCreate.highLimit);
|
|
@@ -832,6 +1129,11 @@
|
|
|
832
1129
|
return;
|
|
833
1130
|
}
|
|
834
1131
|
if (!selectedPath) { panel.append('<div class="opcua-tree-empty">Select a node to edit browseName, namespace, nodeId, and description.</div>'); return; }
|
|
1132
|
+
if (selectedPath.indexOf("virtual:") === 0) {
|
|
1133
|
+
var visualName = getVirtualNodeName(selectedPath);
|
|
1134
|
+
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>');
|
|
1135
|
+
return;
|
|
1136
|
+
}
|
|
835
1137
|
var item = getAtPath(editorState, selectedPath);
|
|
836
1138
|
if (!item) { panel.append('<div class="opcua-tree-empty">Selected node not found.</div>'); return; }
|
|
837
1139
|
var nodeClass = nodeClassFromPath(selectedPath);
|
|
@@ -883,7 +1185,18 @@
|
|
|
883
1185
|
panel.append('<div class="form-row"><label>browseName</label><input type="text" id="opcua-detail-name"></div>');
|
|
884
1186
|
panel.append('<div class="form-row"><label>nodeClass</label><input type="text" id="opcua-detail-class" readonly></div>');
|
|
885
1187
|
panel.append('<div class="form-row"><label>namespace</label><select id="opcua-detail-namespace"></select></div>');
|
|
886
|
-
|
|
1188
|
+
if (nodeClass === "Variable" && !nodeIdLocked) {
|
|
1189
|
+
panel.append('<div class="form-row"><label>nodeId</label>' +
|
|
1190
|
+
'<div class="opcua-nodeid-field">' +
|
|
1191
|
+
'<span class="opcua-nodeid-prefix">ns=' + namespaceId + ';</span>' +
|
|
1192
|
+
'<select id="opcua-detail-nodeid-type" style="width: 70px; flex: 0 0 auto;"><option value="s">s</option><option value="i">i</option></select>' +
|
|
1193
|
+
'<span style="padding: 0 4px; flex: 0 0 auto;">=</span>' +
|
|
1194
|
+
'<input type="text" id="opcua-detail-nodeid-value" style="flex: 1 1 auto; font-family: monospace;">' +
|
|
1195
|
+
'<a href="#" id="opcua-detail-copy-nodeid" class="editor-button editor-button-small"><i class="fa fa-copy"></i> Copy</a>' +
|
|
1196
|
+
'</div></div>');
|
|
1197
|
+
} else {
|
|
1198
|
+
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>');
|
|
1199
|
+
}
|
|
887
1200
|
panel.append('<div class="form-row"><label>Description</label><input type="text" id="opcua-detail-description"></div>');
|
|
888
1201
|
panel.append('<div class="form-row"><label>displayName</label><input type="text" id="opcua-detail-displayname" placeholder="Leave blank to use browseName"></div>');
|
|
889
1202
|
panel.append('<div class="form-row"><label>accessPermission</label>' + buildAccessPermissionSelect("opcua-detail-accesspermission", item.accessPermission || ["public"]) + '</div>');
|
|
@@ -942,6 +1255,7 @@
|
|
|
942
1255
|
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
1256
|
panel.append('<div class="form-row"><label>variablePath</label><input type="text" id="opcua-detail-variable-nodeid"></div>');
|
|
944
1257
|
panel.append('<div class="form-row"><label>severity</label><input type="number" id="opcua-detail-severity"></div>');
|
|
1258
|
+
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
1259
|
if ((item.type || "levelAlarm") === "levelAlarm") {
|
|
946
1260
|
panel.append('<div class="form-row"><label>highHighLimit</label><input type="number" id="opcua-detail-highhighlimit"></div>');
|
|
947
1261
|
panel.append('<div class="form-row"><label>highHighMessage</label><input type="text" id="opcua-detail-highhighmessage"></div>');
|
|
@@ -959,7 +1273,14 @@
|
|
|
959
1273
|
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
1274
|
$("#opcua-detail-name").val(item.name || "");
|
|
961
1275
|
$("#opcua-detail-class").val(nodeClass);
|
|
962
|
-
|
|
1276
|
+
if (nodeClass === "Variable" && !nodeIdLocked) {
|
|
1277
|
+
var parsed = parseNodeId(item.nodeId, buildDefaultNodeIdSuffixFromEditorPath(selectedPath));
|
|
1278
|
+
$("#opcua-detail-nodeid-type").val(parsed.type);
|
|
1279
|
+
$("#opcua-detail-nodeid-value").val(parsed.value);
|
|
1280
|
+
updateNodeIdValueInputState("detail", parsed.type);
|
|
1281
|
+
} else {
|
|
1282
|
+
$("#opcua-detail-nodeid").val(nodeIdLocked ? buildDefaultNodeIdSuffixFromEditorPath(selectedPath) : nodeIdSuffix);
|
|
1283
|
+
}
|
|
963
1284
|
$("#opcua-detail-description").val(item.description || "");
|
|
964
1285
|
namespaceOptions.forEach(function (option) {
|
|
965
1286
|
$("#opcua-detail-namespace").append($("<option></option>").val(option.id).text(getNamespaceLabel(option.id)));
|
|
@@ -973,13 +1294,14 @@
|
|
|
973
1294
|
$("#opcua-detail-alarm-type").val(item.type || "levelAlarm");
|
|
974
1295
|
$("#opcua-detail-variable-nodeid").val(item.variableNodeId || "");
|
|
975
1296
|
$("#opcua-detail-severity").val(item.severity !== undefined ? item.severity : 500);
|
|
976
|
-
$("#opcua-detail-
|
|
1297
|
+
$("#opcua-detail-sendvalue").prop("checked", item.sendValue !== false);
|
|
1298
|
+
$("#opcua-detail-highhighlimit").val(item.highHighLimit !== undefined ? item.highHighLimit : 90);
|
|
977
1299
|
$("#opcua-detail-highhighmessage").val(item.highHighMessage || "High High alarm");
|
|
978
1300
|
$("#opcua-detail-highlimit").val(item.highLimit !== undefined ? item.highLimit : 80);
|
|
979
1301
|
$("#opcua-detail-highmessage").val(item.highMessage || "High alarm");
|
|
980
1302
|
$("#opcua-detail-lowlimit").val(item.lowLimit !== undefined ? item.lowLimit : 20);
|
|
981
1303
|
$("#opcua-detail-lowmessage").val(item.lowMessage || "Low alarm");
|
|
982
|
-
$("#opcua-detail-lowlowlimit").val(item.lowLowLimit !== undefined ? item.lowLowLimit :
|
|
1304
|
+
$("#opcua-detail-lowlowlimit").val(item.lowLowLimit !== undefined ? item.lowLowLimit : 10);
|
|
983
1305
|
$("#opcua-detail-lowlowmessage").val(item.lowLowMessage || "Low Low alarm");
|
|
984
1306
|
$("#opcua-detail-normalstatevalue").val(item.normalStateValue !== undefined ? item.normalStateValue : 0);
|
|
985
1307
|
$("#opcua-detail-digitalmessage").val(item.digitalMessage || "Digital alarm");
|
|
@@ -993,6 +1315,10 @@
|
|
|
993
1315
|
|
|
994
1316
|
function removeNode(path) {
|
|
995
1317
|
if (!path) return;
|
|
1318
|
+
if (path.indexOf("virtual:") === 0) {
|
|
1319
|
+
RED.notify("Visual folders cannot be removed.", "warning");
|
|
1320
|
+
return;
|
|
1321
|
+
}
|
|
996
1322
|
if (nodeClassFromPath(path) === "Namespace") {
|
|
997
1323
|
var namespaceItem = getAtPath(editorState, path);
|
|
998
1324
|
if (normalizeNamespaceId(namespaceItem && namespaceItem.id) === DEFAULT_NAMESPACE_ID) {
|
|
@@ -1246,8 +1572,48 @@
|
|
|
1246
1572
|
|
|
1247
1573
|
$(document).on("contextmenu", ".opcua-tree-row", function (event) {
|
|
1248
1574
|
event.preventDefault();
|
|
1249
|
-
|
|
1250
|
-
|
|
1575
|
+
var path = $(this).attr("data-path");
|
|
1576
|
+
selectNode(path);
|
|
1577
|
+
|
|
1578
|
+
var contextMenu = $("#opcua-tree-context-menu");
|
|
1579
|
+
contextMenu.find("a").hide();
|
|
1580
|
+
|
|
1581
|
+
if (path.indexOf("virtual:") === 0) {
|
|
1582
|
+
if (path === "virtual:Objects") {
|
|
1583
|
+
contextMenu.find('[data-action="add-folder"]').show();
|
|
1584
|
+
contextMenu.find('[data-action="add-object"]').show();
|
|
1585
|
+
} else if (path === "virtual:Types.ObjectTypes") {
|
|
1586
|
+
contextMenu.find('[data-action="add-objecttype"]').show();
|
|
1587
|
+
} else if (path === "virtual:Types.DataTypes") {
|
|
1588
|
+
contextMenu.find('[data-action="add-enumeration"]').show();
|
|
1589
|
+
}
|
|
1590
|
+
} else {
|
|
1591
|
+
var nodeClass = nodeClassFromPath(path);
|
|
1592
|
+
if (nodeClass === "Folder" || nodeClass === "Object" || nodeClass === "ObjectType") {
|
|
1593
|
+
contextMenu.find('[data-action="add-folder"]').show();
|
|
1594
|
+
contextMenu.find('[data-action="add-object"]').show();
|
|
1595
|
+
contextMenu.find('[data-action="add-variable"]').show();
|
|
1596
|
+
contextMenu.find('[data-action="add-enum-variable"]').show();
|
|
1597
|
+
contextMenu.find('[data-action="add-alarm"]').show();
|
|
1598
|
+
contextMenu.find('[data-action="add-method"]').show();
|
|
1599
|
+
contextMenu.find('[data-action="edit"]').show();
|
|
1600
|
+
contextMenu.find('[data-action="remove"]').show();
|
|
1601
|
+
} else if (nodeClass === "Enumeration") {
|
|
1602
|
+
contextMenu.find('[data-action="edit"]').show();
|
|
1603
|
+
contextMenu.find('[data-action="remove"]').show();
|
|
1604
|
+
} else if (nodeClass === "Namespace") {
|
|
1605
|
+
contextMenu.find('[data-action="edit"]').show();
|
|
1606
|
+
var item = getAtPath(editorState, path);
|
|
1607
|
+
if (item && normalizeNamespaceId(item.id) !== DEFAULT_NAMESPACE_ID) {
|
|
1608
|
+
contextMenu.find('[data-action="remove"]').show();
|
|
1609
|
+
}
|
|
1610
|
+
} else {
|
|
1611
|
+
contextMenu.find('[data-action="edit"]').show();
|
|
1612
|
+
contextMenu.find('[data-action="remove"]').show();
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
contextMenu.css({ left: event.clientX + "px", top: event.clientY + "px" }).show();
|
|
1251
1617
|
});
|
|
1252
1618
|
|
|
1253
1619
|
$(document).on("click", function () { $("#opcua-tree-context-menu").hide(); });
|
|
@@ -1344,13 +1710,25 @@
|
|
|
1344
1710
|
var item = getAtPath(editorState, selectedPath);
|
|
1345
1711
|
if (!item) return;
|
|
1346
1712
|
item.namespaceId = nextNamespaceId;
|
|
1347
|
-
|
|
1713
|
+
if (nodeClassFromPath(selectedPath) === "Variable" && !isObjectTypeModelPath(selectedPath)) {
|
|
1714
|
+
saveDetailNodeId(selectedPath);
|
|
1715
|
+
} else {
|
|
1716
|
+
item.nodeId = normalizeCustomNodeIdFromSuffix(selectedPath, $("#opcua-detail-nodeid").val());
|
|
1717
|
+
}
|
|
1348
1718
|
syncStateToJson(false);
|
|
1349
1719
|
renderTree();
|
|
1350
1720
|
renderBreadcrumbs();
|
|
1351
1721
|
renderDetails();
|
|
1352
1722
|
});
|
|
1353
1723
|
$(document).on("input", "#opcua-detail-nodeid", function () { updateNode(selectedPath, { nodeId: normalizeCustomNodeIdFromSuffix(selectedPath, $(this).val()) }); });
|
|
1724
|
+
$(document).on("change", "#opcua-detail-nodeid-type", function () {
|
|
1725
|
+
var type = $(this).val();
|
|
1726
|
+
updateNodeIdValueInputState("detail", type);
|
|
1727
|
+
saveDetailNodeId(selectedPath);
|
|
1728
|
+
});
|
|
1729
|
+
$(document).on("input", "#opcua-detail-nodeid-value", function () {
|
|
1730
|
+
saveDetailNodeId(selectedPath);
|
|
1731
|
+
});
|
|
1354
1732
|
$(document).on("click", "#opcua-detail-copy-nodeid", function (event) {
|
|
1355
1733
|
event.preventDefault();
|
|
1356
1734
|
copyNodeIdValue(buildDisplayNodeIdFromEditorPath(selectedPath));
|
|
@@ -1389,6 +1767,7 @@
|
|
|
1389
1767
|
});
|
|
1390
1768
|
$(document).on("input", "#opcua-detail-variable-nodeid", function () { updateNode(selectedPath, { variableNodeId: $(this).val() }); });
|
|
1391
1769
|
$(document).on("input", "#opcua-detail-severity", function () { updateNode(selectedPath, { severity: Number($(this).val() || 0) }); });
|
|
1770
|
+
$(document).on("change", "#opcua-detail-sendvalue", function () { updateNode(selectedPath, { sendValue: $(this).is(":checked") }); });
|
|
1392
1771
|
$(document).on("input", "#opcua-detail-highhighlimit", function () { updateNode(selectedPath, { highHighLimit: Number($(this).val() || 0) }); });
|
|
1393
1772
|
$(document).on("input", "#opcua-detail-highhighmessage", function () { updateNode(selectedPath, { highHighMessage: $(this).val() }); });
|
|
1394
1773
|
$(document).on("input", "#opcua-detail-highlimit", function () { updateNode(selectedPath, { highLimit: Number($(this).val() || 0) }); });
|
|
@@ -1399,8 +1778,35 @@
|
|
|
1399
1778
|
$(document).on("input", "#opcua-detail-lowlowmessage", function () { updateNode(selectedPath, { lowLowMessage: $(this).val() }); });
|
|
1400
1779
|
$(document).on("input", "#opcua-detail-normalstatevalue", function () { updateNode(selectedPath, { normalStateValue: Number($(this).val() || 0) }); });
|
|
1401
1780
|
$(document).on("input", "#opcua-detail-digitalmessage", function () { updateNode(selectedPath, { digitalMessage: $(this).val() }); });
|
|
1402
|
-
$(document).on("input", "#opcua-create-name", function () {
|
|
1781
|
+
$(document).on("input", "#opcua-create-name", function () {
|
|
1782
|
+
if (pendingCreate) {
|
|
1783
|
+
var oldName = pendingCreate.name;
|
|
1784
|
+
var nextName = $(this).val();
|
|
1785
|
+
pendingCreate.name = nextName;
|
|
1786
|
+
|
|
1787
|
+
if (pendingCreate.kind === "variable" || pendingCreate.kind === "enum-variable") {
|
|
1788
|
+
if (pendingCreate.nodeIdType === "s") {
|
|
1789
|
+
var parentSuffix = buildDefaultNodeIdSuffixFromEditorPath(pendingCreate.parentPath);
|
|
1790
|
+
var oldAutoSuffix = parentSuffix ? parentSuffix + "." + oldName : oldName;
|
|
1791
|
+
var nextAutoSuffix = parentSuffix ? parentSuffix + "." + nextName : nextName;
|
|
1792
|
+
|
|
1793
|
+
if (!pendingCreate.nodeIdValue || pendingCreate.nodeIdValue === oldAutoSuffix) {
|
|
1794
|
+
pendingCreate.nodeIdValue = nextAutoSuffix;
|
|
1795
|
+
$("#opcua-create-nodeid-value").val(nextAutoSuffix);
|
|
1796
|
+
}
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
});
|
|
1403
1801
|
$(document).on("input", "#opcua-create-displayname", function () { if (pendingCreate) pendingCreate.displayName = $(this).val(); });
|
|
1802
|
+
$(document).on("change", "#opcua-create-nodeid-type", function () {
|
|
1803
|
+
var type = $(this).val();
|
|
1804
|
+
if (pendingCreate) pendingCreate.nodeIdType = type;
|
|
1805
|
+
updateNodeIdValueInputState("create", type);
|
|
1806
|
+
});
|
|
1807
|
+
$(document).on("input", "#opcua-create-nodeid-value", function () {
|
|
1808
|
+
if (pendingCreate) pendingCreate.nodeIdValue = $(this).val();
|
|
1809
|
+
});
|
|
1404
1810
|
$(document).on("change", "#opcua-create-accesspermission", function () { if (pendingCreate) pendingCreate.accessPermission = normalizeAccessPermissionValues($(this).val()); });
|
|
1405
1811
|
$(document).on("change", "#opcua-create-type", function () { if (pendingCreate) pendingCreate.dataType = $(this).val(); });
|
|
1406
1812
|
$(document).on("input", "#opcua-create-value", function () { if (pendingCreate) pendingCreate.value = $(this).val(); });
|
|
@@ -1414,6 +1820,7 @@
|
|
|
1414
1820
|
});
|
|
1415
1821
|
$(document).on("input", "#opcua-create-variable-nodeid", function () { if (pendingCreate) pendingCreate.variableNodeId = $(this).val(); });
|
|
1416
1822
|
$(document).on("input", "#opcua-create-severity", function () { if (pendingCreate) pendingCreate.severity = Number($(this).val() || 0); });
|
|
1823
|
+
$(document).on("change", "#opcua-create-sendvalue", function () { if (pendingCreate) pendingCreate.sendValue = $(this).is(":checked"); });
|
|
1417
1824
|
$(document).on("input", "#opcua-create-highhighlimit", function () { if (pendingCreate) pendingCreate.highHighLimit = Number($(this).val() || 0); });
|
|
1418
1825
|
$(document).on("input", "#opcua-create-highhighmessage", function () { if (pendingCreate) pendingCreate.highHighMessage = $(this).val(); });
|
|
1419
1826
|
$(document).on("input", "#opcua-create-highlimit", function () { if (pendingCreate) pendingCreate.highLimit = Number($(this).val() || 0); });
|
|
@@ -1475,9 +1882,12 @@
|
|
|
1475
1882
|
color: "#d9edf7",
|
|
1476
1883
|
credentials: { username: { type: "text" }, password: { type: "password" }, users: { type: "text" }, groups: { type: "text" } },
|
|
1477
1884
|
defaults: {
|
|
1478
|
-
name: { value: "" }, resourcePath: { value: "/" }, serverName: { value: "Node-RED OPC UA Server", required: true }, allowAnonymous: { value: true },
|
|
1885
|
+
name: { value: "" }, resourcePath: { value: "/" }, serverName: { value: "Node-RED OPC UA Server", required: true }, allowAnonymous: { value: true }, automaticallyAcceptUnknownCertificate: { value: true },
|
|
1479
1886
|
port: { value: 4840, required: true, validate: function (value) { var port = Number(value); return Number.isInteger(port) && port > 0 && port < 65536; } },
|
|
1480
1887
|
maxConnections: { value: 10, required: true, validate: function (value) { var n = Number(value); return Number.isInteger(n) && n > 0; } },
|
|
1888
|
+
minSessionTimeout: { value: 100, required: true, validate: function (value) { var n = Number(value); return Number.isInteger(n) && n >= 0; } },
|
|
1889
|
+
defaultSessionTimeout: { value: 30000, required: true, validate: function (value) { var n = Number(value); return Number.isInteger(n) && n >= 0; } },
|
|
1890
|
+
maxSessionTimeout: { value: 3000000, required: true, validate: function (value) { var n = Number(value); return Number.isInteger(n) && n >= 0; } },
|
|
1481
1891
|
securityPolicy: { value: "None", required: true }, securityMode: { value: "None", required: true }, namespaceUri: { value: "urn:node-red:opc-ua-server", required: true },
|
|
1482
1892
|
tree: {
|
|
1483
1893
|
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 +1903,15 @@
|
|
|
1493
1903
|
label: function () { return this.name || this.serverName || "opc-ua-server"; },
|
|
1494
1904
|
oneditprepare: function () {
|
|
1495
1905
|
var node = this;
|
|
1906
|
+
if (!$("#node-input-minSessionTimeout").val()) {
|
|
1907
|
+
$("#node-input-minSessionTimeout").val(100);
|
|
1908
|
+
}
|
|
1909
|
+
if (!$("#node-input-defaultSessionTimeout").val()) {
|
|
1910
|
+
$("#node-input-defaultSessionTimeout").val(30000);
|
|
1911
|
+
}
|
|
1912
|
+
if (!$("#node-input-maxSessionTimeout").val()) {
|
|
1913
|
+
$("#node-input-maxSessionTimeout").val(3000000);
|
|
1914
|
+
}
|
|
1496
1915
|
editorState = cloneTree(normalizeTree(parseTree(node.tree)));
|
|
1497
1916
|
authGroups = normalizeAuthGroups($("#node-input-groups").val());
|
|
1498
1917
|
authUsers = normalizeAuthUsers($("#node-input-users").val());
|
|
@@ -1501,6 +1920,35 @@
|
|
|
1501
1920
|
$("#node-input-namespaceUri").val(defaultNamespaceEntry.name);
|
|
1502
1921
|
}
|
|
1503
1922
|
syncAuthCredentialFields();
|
|
1923
|
+
$("#node-input-securityPolicy").typedInput({
|
|
1924
|
+
types: [
|
|
1925
|
+
{
|
|
1926
|
+
value: "securityPolicy",
|
|
1927
|
+
multiple: "true",
|
|
1928
|
+
options: [
|
|
1929
|
+
{ value: "None", label: "None" },
|
|
1930
|
+
{ value: "Basic128Rsa15", label: "Basic128Rsa15" },
|
|
1931
|
+
{ value: "Basic256", label: "Basic256" },
|
|
1932
|
+
{ value: "Basic256Sha256", label: "Basic256Sha256" },
|
|
1933
|
+
{ value: "Aes128_Sha256_RsaOaep", label: "Aes128_Sha256_RsaOaep" },
|
|
1934
|
+
{ value: "Aes256_Sha256_RsaPss", label: "Aes256_Sha256_RsaPss" }
|
|
1935
|
+
]
|
|
1936
|
+
}
|
|
1937
|
+
]
|
|
1938
|
+
});
|
|
1939
|
+
$("#node-input-securityMode").typedInput({
|
|
1940
|
+
types: [
|
|
1941
|
+
{
|
|
1942
|
+
value: "securityMode",
|
|
1943
|
+
multiple: "true",
|
|
1944
|
+
options: [
|
|
1945
|
+
{ value: "None", label: "None" },
|
|
1946
|
+
{ value: "Sign", label: "Sign" },
|
|
1947
|
+
{ value: "SignAndEncrypt", label: "SignAndEncrypt" }
|
|
1948
|
+
]
|
|
1949
|
+
}
|
|
1950
|
+
]
|
|
1951
|
+
});
|
|
1504
1952
|
updateTreeField(prettyTree(editorState), false);
|
|
1505
1953
|
$("#node-input-tree-editor").typedInput({ type: "json", types: ["json"] });
|
|
1506
1954
|
$("#node-input-tree-editor").typedInput("value", prettyTree(editorState));
|
|
@@ -1524,6 +1972,35 @@
|
|
|
1524
1972
|
$("#node-input-add-auth-group").off("click").on("click", function (event) { event.preventDefault(); addAuthGroup(); });
|
|
1525
1973
|
$("#node-input-add-auth-user").off("click").on("click", function (event) { event.preventDefault(); addAuthUser(); });
|
|
1526
1974
|
|
|
1975
|
+
$("#node-input-open-cert-modal").off("click").on("click", function (event) { event.preventDefault(); openCertModal(); });
|
|
1976
|
+
$("#node-input-close-cert-modal").off("click").on("click", function (event) { event.preventDefault(); closeCertModal(); });
|
|
1977
|
+
$("#node-input-cert-modal").off("click").on("click", function (event) { if (event.target === this) closeCertModal(); });
|
|
1978
|
+
|
|
1979
|
+
$("#node-input-open-settings-modal").off("click").on("click", function (event) { event.preventDefault(); openSettingsModal(); });
|
|
1980
|
+
$("#node-input-close-settings-modal").off("click").on("click", function (event) { event.preventDefault(); closeSettingsModal(); });
|
|
1981
|
+
$("#node-input-settings-modal").off("click").on("click", function (event) { if (event.target === this) closeSettingsModal(); });
|
|
1982
|
+
|
|
1983
|
+
$("#opcua-cert-folders").off("click", ".opcua-cert-item").on("click", ".opcua-cert-item", function () {
|
|
1984
|
+
$("#opcua-cert-folders .opcua-cert-item").removeClass("is-selected");
|
|
1985
|
+
$(this).addClass("is-selected");
|
|
1986
|
+
selectedCertFolder = $(this).attr("data-folder");
|
|
1987
|
+
selectedCertName = "";
|
|
1988
|
+
$("#opcua-cert-details").hide();
|
|
1989
|
+
renderCertificatesList();
|
|
1990
|
+
});
|
|
1991
|
+
|
|
1992
|
+
$("#opcua-cert-files").off("click", ".opcua-cert-item").on("click", ".opcua-cert-item", function () {
|
|
1993
|
+
$("#opcua-cert-files .opcua-cert-item").removeClass("is-selected");
|
|
1994
|
+
$(this).addClass("is-selected");
|
|
1995
|
+
selectedCertName = $(this).attr("data-name");
|
|
1996
|
+
showCertificateDetails();
|
|
1997
|
+
});
|
|
1998
|
+
|
|
1999
|
+
$("#opcua-cert-move-btn").off("click").on("click", function (event) {
|
|
2000
|
+
event.preventDefault();
|
|
2001
|
+
moveCertificate();
|
|
2002
|
+
});
|
|
2003
|
+
|
|
1527
2004
|
$("#node-input-tree-search").off("input").on("input", debounce(function () {
|
|
1528
2005
|
treeSearchValue = $(this).val(); treeSearchTerm = normalizeSearchTerm(treeSearchValue);
|
|
1529
2006
|
$("#node-input-tree-search-clear").toggle(!!treeSearchTerm); renderTree();
|
|
@@ -1546,6 +2023,10 @@
|
|
|
1546
2023
|
closeAuthModal();
|
|
1547
2024
|
return;
|
|
1548
2025
|
}
|
|
2026
|
+
if ($("#node-input-cert-modal").is(":visible")) {
|
|
2027
|
+
closeCertModal();
|
|
2028
|
+
return;
|
|
2029
|
+
}
|
|
1549
2030
|
closeTreeModal();
|
|
1550
2031
|
});
|
|
1551
2032
|
|