@vitormnm/node-red-simple-opcua 1.4.0 → 1.4.1
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 +0 -1
- package/client/icons/opcua.svg +132 -0
- package/client/lib/opcua-client-method-service.js +88 -0
- package/client/lib/opcua-client-write-service.js +1 -1
- package/client/opcua-client-config.js +5 -0
- package/client/opcua-client-help.html +61 -0
- package/client/opcua-client-utils.js +68 -2
- package/client/opcua-client.html +13 -1165
- package/client/opcua-client.js +108 -53
- package/client/testClient.json +18 -0
- package/client/view/opcua-client.css +411 -0
- package/client/view/opcua-client.js +1141 -0
- package/icons/opcua.svg +132 -0
- package/icons/opcua2.svg +132 -0
- package/package.json +3 -3
- package/server/icons/opcua.svg +132 -0
- package/server/lib/opcua-address-space-builder.js +24 -2
- package/server/lib/opcua-server-runtime-child.js +117 -26
- package/server/opcua-server-io.html +1 -1
- package/server/opcua-server-io.js +31 -2
- package/server/opcua-server.html +49 -19
- package/server/opcua-server.js +7 -3
- package/object.json +0 -65
package/client/opcua-client.js
CHANGED
|
@@ -20,18 +20,31 @@ const {
|
|
|
20
20
|
|
|
21
21
|
const { OpcUaClientReadService } = require("./lib/opcua-client-read-service");
|
|
22
22
|
const { OpcUaClientWriteService } = require("./lib/opcua-client-write-service");
|
|
23
|
+
const { OpcUaClientMethodService } = require("./lib/opcua-client-method-service");
|
|
23
24
|
const { OpcUaClientSubscriptionService } = require("./lib/opcua-client-subscription-service");
|
|
24
25
|
const { OpcUaClientSubscriptionIdService } = require("./lib/opcua-client-subscription-id-service");
|
|
26
|
+
const path = require("path");
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
const fs = require("fs");
|
|
30
|
+
const arquivo = path.join(__dirname, "testClient.json");
|
|
25
31
|
|
|
26
32
|
module.exports = function (RED) {
|
|
27
33
|
function OpcUaClientNode(config) {
|
|
28
34
|
RED.nodes.createNode(this, config);
|
|
29
35
|
const node = this;
|
|
30
36
|
|
|
37
|
+
fs.writeFileSync(
|
|
38
|
+
arquivo,
|
|
39
|
+
JSON.stringify(config, null, 2),
|
|
40
|
+
"utf8"
|
|
41
|
+
);
|
|
42
|
+
|
|
31
43
|
node.name = (config.name || "").trim();
|
|
32
44
|
node.connection = RED.nodes.getNode(config.connection);
|
|
33
45
|
node.mode = config.mode || "read";
|
|
34
46
|
node.selectedItems = parseConfiguredItems(config.selectedItems);
|
|
47
|
+
node.methodItems = parseConfiguredMethodsItems(config.selectedItems);
|
|
35
48
|
node.valueProperty = (config.valueProperty || "payload").trim();
|
|
36
49
|
node.valuePropertyType = config.valuePropertyType || "msg";
|
|
37
50
|
node.samplingInterval = Math.max(Number(config.samplingInterval) || 250, 50);
|
|
@@ -42,6 +55,7 @@ module.exports = function (RED) {
|
|
|
42
55
|
const itemsResolver = createItemsResolver(RED);
|
|
43
56
|
const readService = new OpcUaClientReadService();
|
|
44
57
|
const writeService = new OpcUaClientWriteService();
|
|
58
|
+
const methodService = new OpcUaClientMethodService();
|
|
45
59
|
const subscriptionService = new OpcUaClientSubscriptionService();
|
|
46
60
|
const subscriptionIdService = new OpcUaClientSubscriptionIdService();
|
|
47
61
|
|
|
@@ -70,19 +84,24 @@ module.exports = function (RED) {
|
|
|
70
84
|
}
|
|
71
85
|
|
|
72
86
|
const session = await node.connection.getSession();
|
|
87
|
+
|
|
73
88
|
let payload;
|
|
74
89
|
|
|
75
90
|
if (node.mode === "read") {
|
|
91
|
+
|
|
76
92
|
payload = await readService.execute(node, msg, session, itemsResolver);
|
|
77
93
|
node.status({ fill: "green", shape: "dot", text: "read " + payload.length + " nodes" });
|
|
78
94
|
} else if (node.mode === "write") {
|
|
95
|
+
|
|
79
96
|
payload = await writeService.execute(node, msg, session, itemsResolver);
|
|
80
97
|
node.status({ fill: "green", shape: "dot", text: "write " + payload.length + " nodes" });
|
|
81
98
|
} else if (node.mode === "browse") {
|
|
82
99
|
payload = await executeBrowse(node, msg, session);
|
|
83
100
|
node.status({ fill: "green", shape: "dot", text: "browsed " + payload.length + " nodes" });
|
|
84
101
|
} else if (node.mode === "method") {
|
|
85
|
-
payload = await executeMethod(node, msg, session);
|
|
102
|
+
//payload = await executeMethod(node, msg, session);
|
|
103
|
+
|
|
104
|
+
payload = await methodService.execute(node, msg, session, itemsResolver);
|
|
86
105
|
node.status({ fill: "green", shape: "dot", text: "called " + payload.length + " methods" });
|
|
87
106
|
} else if (node.mode === "getSubscriptionId") {
|
|
88
107
|
payload = await subscriptionIdService.execute(node);
|
|
@@ -121,63 +140,14 @@ module.exports = function (RED) {
|
|
|
121
140
|
return payload;
|
|
122
141
|
}
|
|
123
142
|
|
|
124
|
-
async function executeMethod(node, msg, session) {
|
|
125
|
-
const items = ensureArrayPayload(msg, "OPC UA method call");
|
|
126
|
-
const payload = [];
|
|
127
|
-
|
|
128
|
-
for (const item of items) {
|
|
129
|
-
const methodNodeId = resolveMethodId(item);
|
|
130
|
-
|
|
131
|
-
try {
|
|
132
|
-
const objectId = resolveMethodObjectIdFromItem(item) || await resolveMethodObjectId(
|
|
133
|
-
session,
|
|
134
|
-
methodNodeId,
|
|
135
|
-
node.connection.methodObjectIdCache
|
|
136
|
-
);
|
|
137
|
-
const argumentDefinition = await safeGetMethodArgumentDefinition(
|
|
138
|
-
session,
|
|
139
|
-
methodNodeId,
|
|
140
|
-
node.connection.methodDefinitionCache
|
|
141
|
-
);
|
|
142
|
-
const callRequest = {
|
|
143
|
-
objectId,
|
|
144
|
-
methodId: methodNodeId
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
if (Array.isArray(item.inputs) && item.inputs.length > 0) {
|
|
148
|
-
callRequest.inputArguments = item.inputs.map((input) => buildVariantFromItem(input, input.type));
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
const callResult = await session.call(callRequest);
|
|
152
|
-
payload.push(callResultToItemResult(item, callResult, argumentDefinition));
|
|
153
|
-
} catch (itemError) {
|
|
154
|
-
payload.push({
|
|
155
|
-
name: item.name || methodNodeId,
|
|
156
|
-
nodeID: methodNodeId,
|
|
157
|
-
status: itemError.message,
|
|
158
|
-
outputs: []
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
143
|
|
|
163
|
-
return payload;
|
|
164
|
-
}
|
|
165
144
|
|
|
166
|
-
async function safeGetMethodArgumentDefinition(session, methodNodeId, cache) {
|
|
167
|
-
try {
|
|
168
|
-
return await getMethodArgumentDefinition(session, methodNodeId, cache);
|
|
169
|
-
} catch (error) {
|
|
170
|
-
return {
|
|
171
|
-
inputArguments: [],
|
|
172
|
-
outputArguments: []
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
145
|
|
|
177
146
|
function createItemsResolver(REDRuntime) {
|
|
178
147
|
return {
|
|
179
148
|
ensureClientItems,
|
|
180
|
-
ensureWriteItems
|
|
149
|
+
ensureWriteItems,
|
|
150
|
+
ensureMethodItems
|
|
181
151
|
};
|
|
182
152
|
|
|
183
153
|
function ensureClientItems(node, msg, contextName) {
|
|
@@ -206,13 +176,49 @@ module.exports = function (RED) {
|
|
|
206
176
|
return node.selectedItems.map((item, index) => ({
|
|
207
177
|
name: item.name,
|
|
208
178
|
nodeID: item.nodeID,
|
|
209
|
-
type: item.
|
|
179
|
+
type: item.dataType,
|
|
210
180
|
value: resolveWriteValueForItem(node, msg, item, index, configuredValue, REDRuntime)
|
|
211
181
|
}));
|
|
212
182
|
}
|
|
213
183
|
|
|
214
184
|
return ensureArrayPayload(msg, "OPC UA write");
|
|
215
185
|
}
|
|
186
|
+
|
|
187
|
+
function ensureMethodItems(node, msg) {
|
|
188
|
+
const payload = msg ? msg.payload : undefined;
|
|
189
|
+
|
|
190
|
+
if (Array.isArray(payload) && payload.length > 0) {
|
|
191
|
+
return payload;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (node.methodItems?.length > 0) {
|
|
195
|
+
return node.methodItems.map(item => ({
|
|
196
|
+
name: item.name,
|
|
197
|
+
nodeID: item.nodeID,
|
|
198
|
+
objectID: item.objectId,
|
|
199
|
+
inputs: item.inputs.map(input => {
|
|
200
|
+
const value = resolveConfiguredWriteValue(
|
|
201
|
+
{
|
|
202
|
+
...node,
|
|
203
|
+
valueProperty: input.valueProperty,
|
|
204
|
+
valuePropertyType: input.valuePropertyType
|
|
205
|
+
},
|
|
206
|
+
msg,
|
|
207
|
+
REDRuntime
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
return {
|
|
211
|
+
name: input.name,
|
|
212
|
+
type: input.dataType,
|
|
213
|
+
value
|
|
214
|
+
};
|
|
215
|
+
}),
|
|
216
|
+
outputs: item.outputs || []
|
|
217
|
+
}));
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return ensureArrayPayload(msg, "OPC UA method");
|
|
221
|
+
}
|
|
216
222
|
}
|
|
217
223
|
|
|
218
224
|
function normalizeBrowseRoots(node, payload) {
|
|
@@ -268,6 +274,25 @@ module.exports = function (RED) {
|
|
|
268
274
|
return String(methodId).trim();
|
|
269
275
|
}
|
|
270
276
|
|
|
277
|
+
function parseConfiguredMethodsItems(rawValue) {
|
|
278
|
+
try {
|
|
279
|
+
if (!rawValue || typeof rawValue !== "string") {
|
|
280
|
+
return [];
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
const parsed = JSON.parse(rawValue);
|
|
285
|
+
if (!Array.isArray(parsed)) {
|
|
286
|
+
return [];
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return parsed
|
|
290
|
+
|
|
291
|
+
} catch (error) {
|
|
292
|
+
return [];
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
271
296
|
function parseConfiguredItems(rawValue) {
|
|
272
297
|
if (!rawValue || typeof rawValue !== "string") {
|
|
273
298
|
return [];
|
|
@@ -344,6 +369,25 @@ module.exports = function (RED) {
|
|
|
344
369
|
return REDRuntime.util.getMessageProperty(msg, property);
|
|
345
370
|
}
|
|
346
371
|
|
|
372
|
+
function resolveConfiguredMethodValue(node, msg, REDRuntime) {
|
|
373
|
+
const property = node.valueProperty || "payload";
|
|
374
|
+
const type = node.valuePropertyType || "msg";
|
|
375
|
+
|
|
376
|
+
if (type === "msg") {
|
|
377
|
+
return REDRuntime.util.getMessageProperty(msg, property);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (type === "flow") {
|
|
381
|
+
return node.context().flow.get(property);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (type === "global") {
|
|
385
|
+
return node.context().global.get(property);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
return REDRuntime.util.getMessageProperty(msg, property);
|
|
389
|
+
}
|
|
390
|
+
|
|
347
391
|
function selectWriteValueForItem(configuredValue, item, index) {
|
|
348
392
|
if (Array.isArray(configuredValue)) {
|
|
349
393
|
const byName = item && item.name ? configuredValue.find((entry) => entry && entry.name === item.name) : undefined;
|
|
@@ -376,5 +420,16 @@ module.exports = function (RED) {
|
|
|
376
420
|
return configuredValue;
|
|
377
421
|
}
|
|
378
422
|
|
|
423
|
+
RED.httpAdmin.get("/opcua-client-resource/style.css", function (req, res) {
|
|
424
|
+
const cssPath = path.join(__dirname, "view", "opcua-client.css");
|
|
425
|
+
res.sendFile(cssPath);
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
RED.httpAdmin.get("/opcua-client-resource/script.js", function (req, res) {
|
|
429
|
+
const jsPath = path.join(__dirname, "view", "opcua-client.js");
|
|
430
|
+
res.sendFile(jsPath);
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
|
|
379
434
|
RED.nodes.registerType("opcua-client", OpcUaClientNode);
|
|
380
435
|
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "72c8a9a049a63b1c",
|
|
3
|
+
"type": "opcua-client",
|
|
4
|
+
"z": "7a59646bfdbe5f29",
|
|
5
|
+
"name": "client browse",
|
|
6
|
+
"connection": "b09a1570957d3c44",
|
|
7
|
+
"mode": "browse",
|
|
8
|
+
"selectedItems": "[\n {\n \"name\": \"myServer1\",\n \"nodeID\": \"ns=2;s=myServer1\",\n \"nodeClass\": \"Object\",\n \"valueProperty\": \"payload\",\n \"valuePropertyType\": \"msg\"\n }\n]",
|
|
9
|
+
"samplingInterval": 250,
|
|
10
|
+
"publishingInterval": 250,
|
|
11
|
+
"x": 590,
|
|
12
|
+
"y": 1200,
|
|
13
|
+
"wires": [
|
|
14
|
+
[
|
|
15
|
+
"ac4d1f63af2598d4"
|
|
16
|
+
]
|
|
17
|
+
]
|
|
18
|
+
}
|
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
|
|
2
|
+
body.opcua-tree-modal-open {
|
|
3
|
+
overflow: hidden;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.opcua-tree-editor {
|
|
7
|
+
width: 100%;
|
|
8
|
+
min-height: 120px;
|
|
9
|
+
border: 1px solid var(--red-ui-form-input-border-color, #d9d9d9);
|
|
10
|
+
background: var(--red-ui-form-input-background, #fff);
|
|
11
|
+
border-radius: 4px;
|
|
12
|
+
overflow: auto;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.opcua-tree-row {
|
|
16
|
+
display: flex;
|
|
17
|
+
align-items: center;
|
|
18
|
+
gap: 5px;
|
|
19
|
+
min-height: 24px;
|
|
20
|
+
padding: 2px 6px;
|
|
21
|
+
font-size: 12px;
|
|
22
|
+
border-bottom: 1px solid transparent;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.opcua-tree-row:hover {
|
|
26
|
+
background: var(--red-ui-list-item-background-hover, #f3f7fd);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.opcua-tree-row.is-selected {
|
|
30
|
+
background: var(--red-ui-list-item-background-selected, #d9ecff);
|
|
31
|
+
color: var(--red-ui-list-item-color-selected, inherit);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.opcua-tree-indent {
|
|
35
|
+
flex: 0 0 auto;
|
|
36
|
+
width: 14px;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.opcua-tree-twisty {
|
|
40
|
+
width: 16px;
|
|
41
|
+
text-align: center;
|
|
42
|
+
color: #777;
|
|
43
|
+
flex: 0 0 16px;
|
|
44
|
+
cursor: pointer;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.opcua-tree-icon {
|
|
48
|
+
width: 14px;
|
|
49
|
+
text-align: center;
|
|
50
|
+
color: #777;
|
|
51
|
+
flex: 0 0 14px;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.opcua-tree-label {
|
|
55
|
+
flex: 1 1 auto;
|
|
56
|
+
overflow: hidden;
|
|
57
|
+
text-overflow: ellipsis;
|
|
58
|
+
white-space: nowrap;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.opcua-tree-type {
|
|
62
|
+
color: #777;
|
|
63
|
+
font-size: 11px;
|
|
64
|
+
flex: 0 0 auto;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.opcua-tree-title {
|
|
68
|
+
min-width: 100px;
|
|
69
|
+
font-weight: 600;
|
|
70
|
+
color: #444;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.opcua-client-nodeid-label {
|
|
74
|
+
margin-left: auto;
|
|
75
|
+
text-align: right;
|
|
76
|
+
word-break: break-all;
|
|
77
|
+
max-width: 320px;
|
|
78
|
+
color: #777;
|
|
79
|
+
font-size: 12px;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.opcua-tree-actions {
|
|
83
|
+
margin-left: auto;
|
|
84
|
+
display: flex;
|
|
85
|
+
gap: 6px;
|
|
86
|
+
align-items: center;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.opcua-tree-empty {
|
|
90
|
+
padding: 14px;
|
|
91
|
+
border: 1px dashed #d9d9d9;
|
|
92
|
+
border-radius: 4px;
|
|
93
|
+
background: #fafafa;
|
|
94
|
+
color: #777;
|
|
95
|
+
text-align: center;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.opcua-client-json {
|
|
99
|
+
width: 100%;
|
|
100
|
+
box-sizing: border-box;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.opcua-client-tag-box {
|
|
104
|
+
width: 100%;
|
|
105
|
+
min-height: 70px;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.opcua-client-tag-chip {
|
|
109
|
+
display: grid;
|
|
110
|
+
grid-template-columns: 18px 1fr auto;
|
|
111
|
+
grid-template-areas:
|
|
112
|
+
"icon name name"
|
|
113
|
+
"icon write right";
|
|
114
|
+
column-gap: 8px;
|
|
115
|
+
row-gap: 6px;
|
|
116
|
+
align-items: center;
|
|
117
|
+
border: 1px solid #d9d9d9;
|
|
118
|
+
border-radius: 4px;
|
|
119
|
+
background: #fafafa;
|
|
120
|
+
padding: 6px 8px;
|
|
121
|
+
margin-bottom: 6px;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.opcua-client-tag-chip .opcua-tree-icon {
|
|
125
|
+
grid-area: icon;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.opcua-client-tag-chip .opcua-tree-title {
|
|
129
|
+
grid-area: name;
|
|
130
|
+
min-width: 0;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.opcua-client-tag-right {
|
|
134
|
+
grid-area: right;
|
|
135
|
+
display: flex;
|
|
136
|
+
align-items: center;
|
|
137
|
+
gap: 8px;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.opcua-client-tag-right .opcua-client-nodeid-label {
|
|
141
|
+
margin: 0;
|
|
142
|
+
max-width: 320px;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.opcua-client-tag-right .opcua-tree-actions {
|
|
146
|
+
margin: 0;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.opcua-client-tag-chip-meta,
|
|
150
|
+
.opcua-client-description {
|
|
151
|
+
color: #777;
|
|
152
|
+
font-size: 12px;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.opcua-client-tag-write {
|
|
156
|
+
display: flex;
|
|
157
|
+
gap: 8px;
|
|
158
|
+
align-items: center;
|
|
159
|
+
width: 100%;
|
|
160
|
+
margin-top: 0;
|
|
161
|
+
margin-left: 0;
|
|
162
|
+
grid-area: write;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.opcua-client-tag-write input {
|
|
166
|
+
font-size: 12px;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.opcua-client-tag-write input {
|
|
170
|
+
flex: 1 1 auto;
|
|
171
|
+
min-width: 120px;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.opcua-client-tag-write .red-ui-typedInput-container {
|
|
175
|
+
flex: 1 1 auto;
|
|
176
|
+
min-width: 220px;
|
|
177
|
+
width: 100% !important;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
.opcua-client-tag-write .red-ui-typedInput-container input.red-ui-typedInput-input {
|
|
181
|
+
width: calc(100% - 92px);
|
|
182
|
+
min-width: 120px;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.opcua-client-load-row {
|
|
186
|
+
display: flex;
|
|
187
|
+
gap: 8px;
|
|
188
|
+
align-items: center;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.opcua-client-load-row input {
|
|
192
|
+
flex: 1 1 auto;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.opcua-tree-search {
|
|
196
|
+
display: flex;
|
|
197
|
+
align-items: center;
|
|
198
|
+
gap: 8px;
|
|
199
|
+
width: 100%;
|
|
200
|
+
padding: 6px 10px;
|
|
201
|
+
border: 1px solid #d9d9d9;
|
|
202
|
+
border-radius: 6px;
|
|
203
|
+
background: #fafafa;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.opcua-tree-search input {
|
|
207
|
+
flex: 1 1 auto;
|
|
208
|
+
min-width: 120px;
|
|
209
|
+
border: 0;
|
|
210
|
+
outline: 0;
|
|
211
|
+
background: transparent;
|
|
212
|
+
box-shadow: none;
|
|
213
|
+
margin: 0;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.opcua-tree-modal {
|
|
217
|
+
position: fixed;
|
|
218
|
+
inset: 0;
|
|
219
|
+
z-index: 2000;
|
|
220
|
+
background: rgba(0, 0, 0, 0.45);
|
|
221
|
+
padding: 24px;
|
|
222
|
+
box-sizing: border-box;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.opcua-tree-modal__dialog {
|
|
226
|
+
display: flex;
|
|
227
|
+
flex-direction: column;
|
|
228
|
+
width: 100%;
|
|
229
|
+
height: 100%;
|
|
230
|
+
background: #f3f3f3;
|
|
231
|
+
border-radius: 8px;
|
|
232
|
+
overflow: hidden;
|
|
233
|
+
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
.opcua-tree-modal__header,
|
|
237
|
+
.opcua-tree-modal__toolbar {
|
|
238
|
+
display: flex;
|
|
239
|
+
align-items: center;
|
|
240
|
+
flex-wrap: wrap;
|
|
241
|
+
gap: 10px;
|
|
242
|
+
padding: 14px 18px;
|
|
243
|
+
background: #fff;
|
|
244
|
+
border-bottom: 1px solid #d9d9d9;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
.opcua-tree-modal__title {
|
|
248
|
+
font-size: 18px;
|
|
249
|
+
font-weight: 600;
|
|
250
|
+
color: #333;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.opcua-tree-modal__header .editor-button-small {
|
|
254
|
+
margin-left: auto;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
.opcua-tree-modal__body {
|
|
258
|
+
flex: 1 1 auto;
|
|
259
|
+
overflow: auto;
|
|
260
|
+
padding: 18px;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.opcua-tree-context-menu {
|
|
264
|
+
position: fixed;
|
|
265
|
+
z-index: 2200;
|
|
266
|
+
min-width: 170px;
|
|
267
|
+
background: #fff;
|
|
268
|
+
border: 1px solid #d9d9d9;
|
|
269
|
+
border-radius: 6px;
|
|
270
|
+
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.18);
|
|
271
|
+
padding: 6px 0;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
.opcua-tree-context-menu a {
|
|
275
|
+
display: block;
|
|
276
|
+
padding: 7px 12px;
|
|
277
|
+
color: #333;
|
|
278
|
+
text-decoration: none;
|
|
279
|
+
font-size: 12px;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
.opcua-tree-context-menu a:hover {
|
|
283
|
+
background: #f3f7fd;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/* ── Method mode ── */
|
|
287
|
+
|
|
288
|
+
.opcua-client-method-chip {
|
|
289
|
+
border: 1px solid #d9d9d9;
|
|
290
|
+
border-radius: 4px;
|
|
291
|
+
background: #fafafa;
|
|
292
|
+
padding: 6px 8px;
|
|
293
|
+
margin-bottom: 6px;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
.opcua-client-method-header {
|
|
297
|
+
display: grid;
|
|
298
|
+
grid-template-columns: 18px 1fr auto auto;
|
|
299
|
+
column-gap: 8px;
|
|
300
|
+
align-items: center;
|
|
301
|
+
margin-bottom: 2px;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
.opcua-client-method-header .opcua-tree-icon {
|
|
305
|
+
grid-column: 1;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
.opcua-client-method-title {
|
|
309
|
+
font-weight: 600;
|
|
310
|
+
font-size: 12px;
|
|
311
|
+
min-width: 0;
|
|
312
|
+
overflow: hidden;
|
|
313
|
+
text-overflow: ellipsis;
|
|
314
|
+
white-space: nowrap;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
.opcua-client-method-header .opcua-client-nodeid-label {
|
|
318
|
+
font-size: 11px;
|
|
319
|
+
color: #777;
|
|
320
|
+
margin: 0;
|
|
321
|
+
overflow: hidden;
|
|
322
|
+
text-overflow: ellipsis;
|
|
323
|
+
white-space: nowrap;
|
|
324
|
+
max-width: 260px;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
.opcua-client-method-section-label {
|
|
328
|
+
font-size: 11px;
|
|
329
|
+
font-weight: 600;
|
|
330
|
+
color: #555;
|
|
331
|
+
text-transform: uppercase;
|
|
332
|
+
letter-spacing: 0.04em;
|
|
333
|
+
margin: 6px 0 3px 26px;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
.opcua-client-method-inp-chip {
|
|
337
|
+
display: grid;
|
|
338
|
+
grid-template-columns: 18px 1fr auto;
|
|
339
|
+
grid-template-areas:
|
|
340
|
+
"icon name name"
|
|
341
|
+
"icon write right";
|
|
342
|
+
column-gap: 8px;
|
|
343
|
+
row-gap: 4px;
|
|
344
|
+
align-items: center;
|
|
345
|
+
border: 1px solid #e8e8e8;
|
|
346
|
+
border-radius: 4px;
|
|
347
|
+
background: #fff;
|
|
348
|
+
padding: 4px 8px;
|
|
349
|
+
margin-bottom: 4px;
|
|
350
|
+
font-size: 12px;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
.opcua-client-method-inp-chip .opcua-tree-icon {
|
|
354
|
+
grid-area: icon;
|
|
355
|
+
color: #aaa;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
.opcua-client-method-inp-chip .opcua-tree-title {
|
|
359
|
+
grid-area: name;
|
|
360
|
+
min-width: 0;
|
|
361
|
+
font-weight: normal;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
.opcua-client-method-inp-chip .opcua-client-tag-write {
|
|
365
|
+
grid-area: write;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
.opcua-client-method-inp-chip .opcua-client-tag-right {
|
|
369
|
+
grid-area: right;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
.opcua-client-method-out-chip {
|
|
373
|
+
display: grid;
|
|
374
|
+
grid-template-columns: 18px 1fr auto;
|
|
375
|
+
grid-template-areas: "icon name right";
|
|
376
|
+
column-gap: 8px;
|
|
377
|
+
align-items: center;
|
|
378
|
+
border: 1px solid #e8e8e8;
|
|
379
|
+
border-radius: 4px;
|
|
380
|
+
background: #f7f7f7;
|
|
381
|
+
padding: 4px 8px;
|
|
382
|
+
margin-bottom: 4px;
|
|
383
|
+
font-size: 12px;
|
|
384
|
+
color: #999;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
.opcua-client-method-out-chip .opcua-tree-icon {
|
|
388
|
+
grid-area: icon;
|
|
389
|
+
color: #ccc;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
.opcua-client-method-out-chip .opcua-tree-title {
|
|
393
|
+
grid-area: name;
|
|
394
|
+
min-width: 0;
|
|
395
|
+
font-weight: normal;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
.opcua-client-method-out-chip .opcua-client-tag-right {
|
|
399
|
+
grid-area: right;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
.opcua-method-section-row {
|
|
403
|
+
display: flex;
|
|
404
|
+
align-items: center;
|
|
405
|
+
gap: 8px;
|
|
406
|
+
margin: 4px 0 2px 26px;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
.opcua-method-section-row a {
|
|
410
|
+
flex-shrink: 0;
|
|
411
|
+
}
|