@vitormnm/node-red-simple-opcua 1.4.0 → 1.4.2
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 +102 -53
- 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,25 @@ 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
|
+
|
|
25
29
|
|
|
26
30
|
module.exports = function (RED) {
|
|
27
31
|
function OpcUaClientNode(config) {
|
|
28
32
|
RED.nodes.createNode(this, config);
|
|
29
33
|
const node = this;
|
|
30
34
|
|
|
35
|
+
|
|
36
|
+
|
|
31
37
|
node.name = (config.name || "").trim();
|
|
32
38
|
node.connection = RED.nodes.getNode(config.connection);
|
|
33
39
|
node.mode = config.mode || "read";
|
|
34
40
|
node.selectedItems = parseConfiguredItems(config.selectedItems);
|
|
41
|
+
node.methodItems = parseConfiguredMethodsItems(config.selectedItems);
|
|
35
42
|
node.valueProperty = (config.valueProperty || "payload").trim();
|
|
36
43
|
node.valuePropertyType = config.valuePropertyType || "msg";
|
|
37
44
|
node.samplingInterval = Math.max(Number(config.samplingInterval) || 250, 50);
|
|
@@ -42,6 +49,7 @@ module.exports = function (RED) {
|
|
|
42
49
|
const itemsResolver = createItemsResolver(RED);
|
|
43
50
|
const readService = new OpcUaClientReadService();
|
|
44
51
|
const writeService = new OpcUaClientWriteService();
|
|
52
|
+
const methodService = new OpcUaClientMethodService();
|
|
45
53
|
const subscriptionService = new OpcUaClientSubscriptionService();
|
|
46
54
|
const subscriptionIdService = new OpcUaClientSubscriptionIdService();
|
|
47
55
|
|
|
@@ -70,19 +78,24 @@ module.exports = function (RED) {
|
|
|
70
78
|
}
|
|
71
79
|
|
|
72
80
|
const session = await node.connection.getSession();
|
|
81
|
+
|
|
73
82
|
let payload;
|
|
74
83
|
|
|
75
84
|
if (node.mode === "read") {
|
|
85
|
+
|
|
76
86
|
payload = await readService.execute(node, msg, session, itemsResolver);
|
|
77
87
|
node.status({ fill: "green", shape: "dot", text: "read " + payload.length + " nodes" });
|
|
78
88
|
} else if (node.mode === "write") {
|
|
89
|
+
|
|
79
90
|
payload = await writeService.execute(node, msg, session, itemsResolver);
|
|
80
91
|
node.status({ fill: "green", shape: "dot", text: "write " + payload.length + " nodes" });
|
|
81
92
|
} else if (node.mode === "browse") {
|
|
82
93
|
payload = await executeBrowse(node, msg, session);
|
|
83
94
|
node.status({ fill: "green", shape: "dot", text: "browsed " + payload.length + " nodes" });
|
|
84
95
|
} else if (node.mode === "method") {
|
|
85
|
-
payload = await executeMethod(node, msg, session);
|
|
96
|
+
//payload = await executeMethod(node, msg, session);
|
|
97
|
+
|
|
98
|
+
payload = await methodService.execute(node, msg, session, itemsResolver);
|
|
86
99
|
node.status({ fill: "green", shape: "dot", text: "called " + payload.length + " methods" });
|
|
87
100
|
} else if (node.mode === "getSubscriptionId") {
|
|
88
101
|
payload = await subscriptionIdService.execute(node);
|
|
@@ -121,63 +134,14 @@ module.exports = function (RED) {
|
|
|
121
134
|
return payload;
|
|
122
135
|
}
|
|
123
136
|
|
|
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
137
|
|
|
163
|
-
return payload;
|
|
164
|
-
}
|
|
165
138
|
|
|
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
139
|
|
|
177
140
|
function createItemsResolver(REDRuntime) {
|
|
178
141
|
return {
|
|
179
142
|
ensureClientItems,
|
|
180
|
-
ensureWriteItems
|
|
143
|
+
ensureWriteItems,
|
|
144
|
+
ensureMethodItems
|
|
181
145
|
};
|
|
182
146
|
|
|
183
147
|
function ensureClientItems(node, msg, contextName) {
|
|
@@ -206,13 +170,49 @@ module.exports = function (RED) {
|
|
|
206
170
|
return node.selectedItems.map((item, index) => ({
|
|
207
171
|
name: item.name,
|
|
208
172
|
nodeID: item.nodeID,
|
|
209
|
-
type: item.
|
|
173
|
+
type: item.dataType,
|
|
210
174
|
value: resolveWriteValueForItem(node, msg, item, index, configuredValue, REDRuntime)
|
|
211
175
|
}));
|
|
212
176
|
}
|
|
213
177
|
|
|
214
178
|
return ensureArrayPayload(msg, "OPC UA write");
|
|
215
179
|
}
|
|
180
|
+
|
|
181
|
+
function ensureMethodItems(node, msg) {
|
|
182
|
+
const payload = msg ? msg.payload : undefined;
|
|
183
|
+
|
|
184
|
+
if (Array.isArray(payload) && payload.length > 0) {
|
|
185
|
+
return payload;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (node.methodItems?.length > 0) {
|
|
189
|
+
return node.methodItems.map(item => ({
|
|
190
|
+
name: item.name,
|
|
191
|
+
nodeID: item.nodeID,
|
|
192
|
+
objectID: item.objectId,
|
|
193
|
+
inputs: item.inputs.map(input => {
|
|
194
|
+
const value = resolveConfiguredWriteValue(
|
|
195
|
+
{
|
|
196
|
+
...node,
|
|
197
|
+
valueProperty: input.valueProperty,
|
|
198
|
+
valuePropertyType: input.valuePropertyType
|
|
199
|
+
},
|
|
200
|
+
msg,
|
|
201
|
+
REDRuntime
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
return {
|
|
205
|
+
name: input.name,
|
|
206
|
+
type: input.dataType,
|
|
207
|
+
value
|
|
208
|
+
};
|
|
209
|
+
}),
|
|
210
|
+
outputs: item.outputs || []
|
|
211
|
+
}));
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return ensureArrayPayload(msg, "OPC UA method");
|
|
215
|
+
}
|
|
216
216
|
}
|
|
217
217
|
|
|
218
218
|
function normalizeBrowseRoots(node, payload) {
|
|
@@ -268,6 +268,25 @@ module.exports = function (RED) {
|
|
|
268
268
|
return String(methodId).trim();
|
|
269
269
|
}
|
|
270
270
|
|
|
271
|
+
function parseConfiguredMethodsItems(rawValue) {
|
|
272
|
+
try {
|
|
273
|
+
if (!rawValue || typeof rawValue !== "string") {
|
|
274
|
+
return [];
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
const parsed = JSON.parse(rawValue);
|
|
279
|
+
if (!Array.isArray(parsed)) {
|
|
280
|
+
return [];
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return parsed
|
|
284
|
+
|
|
285
|
+
} catch (error) {
|
|
286
|
+
return [];
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
271
290
|
function parseConfiguredItems(rawValue) {
|
|
272
291
|
if (!rawValue || typeof rawValue !== "string") {
|
|
273
292
|
return [];
|
|
@@ -344,6 +363,25 @@ module.exports = function (RED) {
|
|
|
344
363
|
return REDRuntime.util.getMessageProperty(msg, property);
|
|
345
364
|
}
|
|
346
365
|
|
|
366
|
+
function resolveConfiguredMethodValue(node, msg, REDRuntime) {
|
|
367
|
+
const property = node.valueProperty || "payload";
|
|
368
|
+
const type = node.valuePropertyType || "msg";
|
|
369
|
+
|
|
370
|
+
if (type === "msg") {
|
|
371
|
+
return REDRuntime.util.getMessageProperty(msg, property);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
if (type === "flow") {
|
|
375
|
+
return node.context().flow.get(property);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
if (type === "global") {
|
|
379
|
+
return node.context().global.get(property);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
return REDRuntime.util.getMessageProperty(msg, property);
|
|
383
|
+
}
|
|
384
|
+
|
|
347
385
|
function selectWriteValueForItem(configuredValue, item, index) {
|
|
348
386
|
if (Array.isArray(configuredValue)) {
|
|
349
387
|
const byName = item && item.name ? configuredValue.find((entry) => entry && entry.name === item.name) : undefined;
|
|
@@ -376,5 +414,16 @@ module.exports = function (RED) {
|
|
|
376
414
|
return configuredValue;
|
|
377
415
|
}
|
|
378
416
|
|
|
417
|
+
RED.httpAdmin.get("/opcua-client-resource/style.css", function (req, res) {
|
|
418
|
+
const cssPath = path.join(__dirname, "view", "opcua-client.css");
|
|
419
|
+
res.sendFile(cssPath);
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
RED.httpAdmin.get("/opcua-client-resource/script.js", function (req, res) {
|
|
423
|
+
const jsPath = path.join(__dirname, "view", "opcua-client.js");
|
|
424
|
+
res.sendFile(jsPath);
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
|
|
379
428
|
RED.nodes.registerType("opcua-client", OpcUaClientNode);
|
|
380
429
|
};
|
|
@@ -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
|
+
}
|