@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
|
@@ -214,6 +214,8 @@ class OpcUaServerProcess {
|
|
|
214
214
|
msg.payload = result.payload;
|
|
215
215
|
this.assignReadMetadata(msg, identifierType, result.identifiers);
|
|
216
216
|
|
|
217
|
+
|
|
218
|
+
|
|
217
219
|
if (result.identifiers.length === 1) {
|
|
218
220
|
msg.topic = result.identifiers[0];
|
|
219
221
|
}
|
|
@@ -306,49 +308,137 @@ class OpcUaServerProcess {
|
|
|
306
308
|
}
|
|
307
309
|
|
|
308
310
|
|
|
309
|
-
|
|
310
311
|
writeFromPayload(msg, nodeId) {
|
|
311
312
|
try {
|
|
312
|
-
|
|
313
|
-
|
|
313
|
+
let writtenPaths = null;
|
|
314
|
+
let payload = msg ? msg.payload : undefined;
|
|
315
|
+
|
|
314
316
|
const target = msg && msg.opcuaServerIo ? msg.opcuaServerIo : {};
|
|
315
317
|
const identifierType = this.resolveIdentifierType(target);
|
|
316
318
|
|
|
319
|
+
// Buffer serializado pelo IPC
|
|
320
|
+
if (
|
|
321
|
+
payload &&
|
|
322
|
+
typeof payload === "object" &&
|
|
323
|
+
payload.type === "Buffer" &&
|
|
324
|
+
Array.isArray(payload.data)
|
|
325
|
+
) {
|
|
326
|
+
payload = Buffer.from(payload.data);
|
|
327
|
+
}
|
|
317
328
|
|
|
329
|
+
const dataType =
|
|
330
|
+
target.dataType ||
|
|
331
|
+
target.type ||
|
|
332
|
+
target.builtInType ||
|
|
333
|
+
"";
|
|
334
|
+
|
|
335
|
+
const isByteString =
|
|
336
|
+
typeof dataType === "string" &&
|
|
337
|
+
dataType.toLowerCase() === "bytestring";
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
// Buffer ou Uint8Array
|
|
342
|
+
if (Buffer.isBuffer(payload) || payload instanceof Uint8Array) {
|
|
343
|
+
|
|
344
|
+
const identifier = this.resolveIdentifier(target);
|
|
345
|
+
|
|
346
|
+
this.node.writeValue(
|
|
347
|
+
identifierType,
|
|
348
|
+
identifier,
|
|
349
|
+
Buffer.isBuffer(payload)
|
|
350
|
+
? payload
|
|
351
|
+
: Buffer.from(payload)
|
|
352
|
+
);
|
|
353
|
+
|
|
354
|
+
writtenPaths = [identifier];
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// Array de números
|
|
358
|
+
else if (
|
|
359
|
+
Array.isArray(payload) &&
|
|
360
|
+
payload.every(item => typeof item === "number")
|
|
361
|
+
) {
|
|
362
|
+
|
|
363
|
+
const identifier = this.resolveIdentifier(target);
|
|
364
|
+
|
|
365
|
+
this.node.writeValue(
|
|
366
|
+
identifierType,
|
|
367
|
+
identifier,
|
|
368
|
+
isByteString
|
|
369
|
+
? Buffer.from(payload)
|
|
370
|
+
: payload
|
|
371
|
+
);
|
|
372
|
+
|
|
373
|
+
writtenPaths = [identifier];
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// Array de objetos
|
|
377
|
+
else if (Array.isArray(payload)) {
|
|
318
378
|
|
|
319
|
-
if (Array.isArray(payload)) {
|
|
320
379
|
if (!payload.length) {
|
|
321
380
|
throw new Error("msg.payload array does not contain any items");
|
|
322
381
|
}
|
|
323
382
|
|
|
324
|
-
payload.forEach(
|
|
383
|
+
payload.forEach(item => {
|
|
325
384
|
this.writePayloadItem(identifierType, item);
|
|
326
385
|
});
|
|
327
386
|
|
|
328
|
-
writtenPaths = payload.map(
|
|
329
|
-
|
|
387
|
+
writtenPaths = payload.map(item =>
|
|
388
|
+
this.resolvePayloadItemIdentifier(item)
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// Objeto { path: value }
|
|
393
|
+
else if (
|
|
394
|
+
payload &&
|
|
395
|
+
typeof payload === "object" &&
|
|
396
|
+
!Array.isArray(payload)
|
|
397
|
+
) {
|
|
330
398
|
|
|
331
399
|
const identifiers = Object.keys(payload);
|
|
400
|
+
|
|
332
401
|
if (!identifiers.length) {
|
|
333
|
-
throw new Error(
|
|
402
|
+
throw new Error(
|
|
403
|
+
"msg.payload object does not contain any " +
|
|
404
|
+
this.getIdentifierLabel(identifierType)
|
|
405
|
+
);
|
|
334
406
|
}
|
|
335
407
|
|
|
336
|
-
identifiers.forEach(
|
|
337
|
-
this.node.writeValue(
|
|
408
|
+
identifiers.forEach(identifier => {
|
|
409
|
+
this.node.writeValue(
|
|
410
|
+
identifierType,
|
|
411
|
+
identifier,
|
|
412
|
+
payload[identifier]
|
|
413
|
+
);
|
|
338
414
|
});
|
|
339
415
|
|
|
340
416
|
writtenPaths = identifiers;
|
|
341
|
-
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// Valor simples
|
|
420
|
+
else {
|
|
342
421
|
|
|
343
422
|
const identifier = this.resolveIdentifier(target);
|
|
344
|
-
this.node.writeValue(identifierType, identifier, payload);
|
|
345
423
|
|
|
424
|
+
this.node.writeValue(
|
|
425
|
+
identifierType,
|
|
426
|
+
identifier,
|
|
427
|
+
payload
|
|
428
|
+
);
|
|
346
429
|
|
|
347
|
-
writtenPaths = [identifier]
|
|
430
|
+
writtenPaths = [identifier];
|
|
348
431
|
}
|
|
349
432
|
|
|
350
433
|
msg.opcua = msg.opcua || {};
|
|
351
|
-
|
|
434
|
+
|
|
435
|
+
this.assignWriteMetadata(
|
|
436
|
+
msg,
|
|
437
|
+
identifierType,
|
|
438
|
+
writtenPaths
|
|
439
|
+
);
|
|
440
|
+
|
|
441
|
+
|
|
352
442
|
if (writtenPaths.length === 1) {
|
|
353
443
|
msg.topic = writtenPaths[0];
|
|
354
444
|
}
|
|
@@ -356,7 +446,7 @@ class OpcUaServerProcess {
|
|
|
356
446
|
process.send({
|
|
357
447
|
type: "send",
|
|
358
448
|
data: msg,
|
|
359
|
-
nodeId
|
|
449
|
+
nodeId
|
|
360
450
|
});
|
|
361
451
|
|
|
362
452
|
process.send({
|
|
@@ -364,28 +454,29 @@ class OpcUaServerProcess {
|
|
|
364
454
|
data: {
|
|
365
455
|
fill: "green",
|
|
366
456
|
shape: "dot",
|
|
367
|
-
text:
|
|
457
|
+
text:
|
|
458
|
+
writtenPaths.length > 1
|
|
459
|
+
? `write ${writtenPaths.length} tags`
|
|
460
|
+
: `write ${writtenPaths[0]}`
|
|
368
461
|
},
|
|
369
|
-
nodeId
|
|
462
|
+
nodeId
|
|
370
463
|
});
|
|
371
464
|
|
|
372
|
-
|
|
373
465
|
} catch (error) {
|
|
374
466
|
|
|
375
467
|
process.send({
|
|
376
468
|
type: "error",
|
|
377
|
-
data: {
|
|
469
|
+
data: {
|
|
470
|
+
fill: "red",
|
|
471
|
+
shape: "ring",
|
|
472
|
+
text: "failed write"
|
|
473
|
+
},
|
|
378
474
|
error: error.message,
|
|
379
|
-
nodeId
|
|
475
|
+
nodeId
|
|
380
476
|
});
|
|
381
477
|
}
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
//return [path];
|
|
386
478
|
}
|
|
387
479
|
|
|
388
|
-
|
|
389
480
|
resolveIdentifierType(target) {
|
|
390
481
|
return target && target.identifierType === "nodeId" ? "nodeId" : "path";
|
|
391
482
|
}
|
|
@@ -726,4 +817,4 @@ process.on("unhandledRejection", (reason) => {
|
|
|
726
817
|
data: "Unhandled Rejection: " + (reason?.message || reason),
|
|
727
818
|
nodeId: nodeId
|
|
728
819
|
});
|
|
729
|
-
});
|
|
820
|
+
});
|
|
@@ -12,10 +12,19 @@ module.exports = function (RED) {
|
|
|
12
12
|
}
|
|
13
13
|
});
|
|
14
14
|
|
|
15
|
+
|
|
16
|
+
const path = require("path");
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
15
23
|
function OpcUaServerIoNode(config) {
|
|
16
24
|
RED.nodes.createNode(this, config);
|
|
17
25
|
const node = this;
|
|
18
26
|
|
|
27
|
+
|
|
19
28
|
node.name = (config.name || "").trim();
|
|
20
29
|
node.serverRef = (config.serverRef || "").trim();
|
|
21
30
|
node.mode = config.mode || "read";
|
|
@@ -106,6 +115,20 @@ module.exports = function (RED) {
|
|
|
106
115
|
});
|
|
107
116
|
}
|
|
108
117
|
|
|
118
|
+
function restoreBuffers(value) {
|
|
119
|
+
// Formato exato que o Node.js IPC gera ao serializar um Buffer
|
|
120
|
+
if (
|
|
121
|
+
value !== null &&
|
|
122
|
+
typeof value === "object" &&
|
|
123
|
+
value.type === "Buffer" &&
|
|
124
|
+
Array.isArray(value.data)
|
|
125
|
+
) {
|
|
126
|
+
return Buffer.from(value.data);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return value;
|
|
130
|
+
}
|
|
131
|
+
|
|
109
132
|
function onMessage(msg, node) {
|
|
110
133
|
if (msg.nodeId == node.id) {
|
|
111
134
|
|
|
@@ -115,7 +138,13 @@ module.exports = function (RED) {
|
|
|
115
138
|
}
|
|
116
139
|
|
|
117
140
|
if (msg.type === "send") {
|
|
118
|
-
|
|
141
|
+
const data = msg.data;
|
|
142
|
+
|
|
143
|
+
if (data && data.payload !== undefined) {
|
|
144
|
+
data.payload = restoreBuffers(data.payload);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
node.send(data);
|
|
119
148
|
}
|
|
120
149
|
|
|
121
150
|
if (msg.type === "error") {
|
|
@@ -132,7 +161,7 @@ module.exports = function (RED) {
|
|
|
132
161
|
});
|
|
133
162
|
|
|
134
163
|
node.send({
|
|
135
|
-
topic
|
|
164
|
+
topic: msg.data.nodeId,
|
|
136
165
|
payload: msg.data.inputArguments,
|
|
137
166
|
opcua: {
|
|
138
167
|
server: msg.data.serverName,
|
package/server/opcua-server.html
CHANGED
|
@@ -238,11 +238,37 @@
|
|
|
238
238
|
color: var(--red-ui-list-item-color-selected, inherit);
|
|
239
239
|
}
|
|
240
240
|
|
|
241
|
-
.opcua-tree-indent {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
241
|
+
.opcua-tree-indent {
|
|
242
|
+
flex: 0 0 auto;
|
|
243
|
+
width: 14px;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
.opcua-tree-twisty {
|
|
247
|
+
width: 16px;
|
|
248
|
+
text-align: center;
|
|
249
|
+
color: #777;
|
|
250
|
+
flex: 0 0 16px;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.opcua-tree-icon {
|
|
254
|
+
width: 14px;
|
|
255
|
+
text-align: center;
|
|
256
|
+
color: #777;
|
|
257
|
+
flex: 0 0 14px;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.opcua-tree-label {
|
|
261
|
+
flex: 1 1 auto;
|
|
262
|
+
overflow: hidden;
|
|
263
|
+
text-overflow: ellipsis;
|
|
264
|
+
white-space: nowrap;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
.opcua-tree-type {
|
|
268
|
+
color: #777;
|
|
269
|
+
font-size: 11px;
|
|
270
|
+
flex: 0 0 auto;
|
|
271
|
+
}
|
|
246
272
|
|
|
247
273
|
.opcua-tree-context-menu {
|
|
248
274
|
position: fixed;
|
|
@@ -251,7 +277,7 @@
|
|
|
251
277
|
border: 1px solid var(--red-ui-form-input-border-color, #d9d9d9);
|
|
252
278
|
border-radius: 4px;
|
|
253
279
|
background: var(--red-ui-form-input-background, #fff);
|
|
254
|
-
box-shadow: 0 6px 18px rgba(0,0,0,0.18);
|
|
280
|
+
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.18);
|
|
255
281
|
overflow: hidden;
|
|
256
282
|
}
|
|
257
283
|
|
|
@@ -503,7 +529,9 @@
|
|
|
503
529
|
margin-left: 0;
|
|
504
530
|
}
|
|
505
531
|
|
|
506
|
-
.opcua-tree-layout {
|
|
532
|
+
.opcua-tree-layout {
|
|
533
|
+
grid-template-columns: 1fr;
|
|
534
|
+
}
|
|
507
535
|
|
|
508
536
|
.opcua-tree-header {
|
|
509
537
|
align-items: flex-start;
|
|
@@ -644,7 +672,7 @@
|
|
|
644
672
|
};
|
|
645
673
|
}
|
|
646
674
|
|
|
647
|
-
|
|
675
|
+
function normalizeBranch(branch) {
|
|
648
676
|
branch = branch || {};
|
|
649
677
|
return {
|
|
650
678
|
name: branch.name ? String(branch.name) : "",
|
|
@@ -745,7 +773,7 @@
|
|
|
745
773
|
var cv = String(currentValue || "");
|
|
746
774
|
var opts = "";
|
|
747
775
|
var noneSelected = (cv === "") ? " selected" : "";
|
|
748
|
-
opts += "<option value=\"\""
|
|
776
|
+
opts += "<option value=\"\"" + noneSelected + ">\u2014 none \u2014</option>";
|
|
749
777
|
names.forEach(function (n) {
|
|
750
778
|
var esc = n.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
751
779
|
var sel = (n === cv) ? " selected" : "";
|
|
@@ -770,8 +798,8 @@
|
|
|
770
798
|
var tokens = pathToTokens(path);
|
|
771
799
|
var current = editorState;
|
|
772
800
|
var parts = [];
|
|
773
|
-
|
|
774
|
-
|
|
801
|
+
// var preservedCollections = { alarms: true, methods: true, objectsTypes: true };
|
|
802
|
+
var preservedCollections = { alarms: true, methods: true };
|
|
775
803
|
|
|
776
804
|
tokens.forEach(function (token) {
|
|
777
805
|
if (/^\d+$/.test(token)) {
|
|
@@ -787,7 +815,7 @@
|
|
|
787
815
|
return parts.join(".");
|
|
788
816
|
}
|
|
789
817
|
|
|
790
|
-
|
|
818
|
+
|
|
791
819
|
function nodeIdSuffixFromValue(nodeId, defaultSuffix) {
|
|
792
820
|
var raw = String(nodeId || "").trim();
|
|
793
821
|
if (!raw) return defaultSuffix;
|
|
@@ -1059,9 +1087,9 @@
|
|
|
1059
1087
|
? (parentPath + ".objectsTypes")
|
|
1060
1088
|
: kind === "alarm"
|
|
1061
1089
|
? (parentPath + ".alarms")
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1090
|
+
: kind === "method"
|
|
1091
|
+
? (parentPath + ".methods")
|
|
1092
|
+
: (parentPath + ".objects");
|
|
1065
1093
|
var target = getAtPath(editorState, branchTargetPath);
|
|
1066
1094
|
if (!Array.isArray(target)) return;
|
|
1067
1095
|
if (kind === "variable") {
|
|
@@ -1197,7 +1225,7 @@
|
|
|
1197
1225
|
panel.append('<div class="form-row"><label>objectsType</label>' + buildObjectTypeSelect("opcua-detail-objectstype", item.objectsType || "") + '</div>');
|
|
1198
1226
|
}
|
|
1199
1227
|
if (nodeClass === "Variable") {
|
|
1200
|
-
panel.append('<div class="form-row"><label>dataType</label><select id="opcua-detail-type"><option value="Int16">Int16</option><option value="Int32">Int32</option><option value="Float">Float</option><option value="Boolean">Boolean</option><option value="String">String</option></select></div>');
|
|
1228
|
+
panel.append('<div class="form-row"><label>dataType</label><select id="opcua-detail-type"><option value="Int16">Int16</option><option value="Int32">Int32</option><option value="Float">Float</option><option value="Boolean">Boolean</option><option value="String">String</option><option value="ByteString">ByteString</option></select></div>');
|
|
1201
1229
|
panel.append('<div class="form-row"><label>Value</label><input type="text" id="opcua-detail-value"></div>');
|
|
1202
1230
|
panel.append('<div class="form-row"><label>Access</label><select id="opcua-detail-access"><option value="readwrite">readwrite</option><option value="readonly">readonly</option></select></div>');
|
|
1203
1231
|
}
|
|
@@ -1239,7 +1267,7 @@
|
|
|
1239
1267
|
});
|
|
1240
1268
|
panel.append('<div class="form-row"><label></label><a href="#" class="editor-button editor-button-small" id="opcua-method-add-output"><i class="fa fa-plus"></i> Add output</a></div>');
|
|
1241
1269
|
}
|
|
1242
|
-
|
|
1270
|
+
if (nodeClass === "Alarm") {
|
|
1243
1271
|
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>');
|
|
1244
1272
|
panel.append('<div class="form-row"><label>variablePath</label><input type="text" id="opcua-detail-variable-nodeid"></div>');
|
|
1245
1273
|
panel.append('<div class="form-row"><label>severity</label><input type="number" id="opcua-detail-severity"></div>');
|
|
@@ -1423,7 +1451,7 @@
|
|
|
1423
1451
|
|
|
1424
1452
|
$(document).on("input", "#opcua-detail-displayname", function () { updateNode(selectedPath, { displayName: $(this).val() }); });
|
|
1425
1453
|
|
|
1426
|
-
|
|
1454
|
+
$(document).on("input", "#opcua-detail-name", function () { updateNode(selectedPath, { name: $(this).val() }); });
|
|
1427
1455
|
$(document).on("change", "#opcua-detail-namespace", function () {
|
|
1428
1456
|
var nextNamespaceId = normalizeNamespaceId($(this).val());
|
|
1429
1457
|
var item = getAtPath(editorState, selectedPath);
|
|
@@ -1527,7 +1555,9 @@
|
|
|
1527
1555
|
}
|
|
1528
1556
|
}
|
|
1529
1557
|
},
|
|
1530
|
-
inputs: 1,
|
|
1558
|
+
inputs: 1,
|
|
1559
|
+
outputs: 0,
|
|
1560
|
+
icon: "opcua.svg",
|
|
1531
1561
|
label: function () { return this.name || this.serverName || "opc-ua-server"; },
|
|
1532
1562
|
oneditprepare: function () {
|
|
1533
1563
|
var node = this;
|
package/server/opcua-server.js
CHANGED
|
@@ -9,6 +9,8 @@ module.exports = function (RED) {
|
|
|
9
9
|
const path = require("path");
|
|
10
10
|
|
|
11
11
|
|
|
12
|
+
|
|
13
|
+
|
|
12
14
|
|
|
13
15
|
|
|
14
16
|
|
|
@@ -16,9 +18,11 @@ module.exports = function (RED) {
|
|
|
16
18
|
RED.nodes.createNode(this, config);
|
|
17
19
|
const node = this;
|
|
18
20
|
const parser = new OpcUaServerConfigParser(node);
|
|
19
|
-
|
|
21
|
+
|
|
20
22
|
const settings = parser.parseNodeConfig(config, this.credentials || {});
|
|
21
23
|
|
|
24
|
+
|
|
25
|
+
|
|
22
26
|
node.name = settings.name;
|
|
23
27
|
node.serverName = settings.serverName;
|
|
24
28
|
node.server = null;
|
|
@@ -58,7 +62,7 @@ module.exports = function (RED) {
|
|
|
58
62
|
});
|
|
59
63
|
|
|
60
64
|
|
|
61
|
-
|
|
65
|
+
|
|
62
66
|
child.send({
|
|
63
67
|
type: "createServer",
|
|
64
68
|
config: config,
|
|
@@ -70,7 +74,7 @@ module.exports = function (RED) {
|
|
|
70
74
|
child.on("message", (msg) => {
|
|
71
75
|
if (msg.nodeId == node.id) {
|
|
72
76
|
if (msg.type === "status") {
|
|
73
|
-
|
|
77
|
+
|
|
74
78
|
node.status(msg.data); // aqui sim usa o node
|
|
75
79
|
}
|
|
76
80
|
|
package/object.json
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"objects": [],
|
|
3
|
-
"folders": [
|
|
4
|
-
{
|
|
5
|
-
"name": "site",
|
|
6
|
-
"folders": [
|
|
7
|
-
{
|
|
8
|
-
"name": "line1",
|
|
9
|
-
"folders": [],
|
|
10
|
-
"objects": [
|
|
11
|
-
{
|
|
12
|
-
"name": "motor",
|
|
13
|
-
"folders": [],
|
|
14
|
-
"objects": [],
|
|
15
|
-
"variables": [
|
|
16
|
-
{
|
|
17
|
-
"name": "speed",
|
|
18
|
-
"type": "Float",
|
|
19
|
-
"value": "",
|
|
20
|
-
"access": "readonly",
|
|
21
|
-
"description": "",
|
|
22
|
-
"displayName": ""
|
|
23
|
-
}
|
|
24
|
-
],
|
|
25
|
-
"method": [
|
|
26
|
-
{
|
|
27
|
-
"name": "cmd_on",
|
|
28
|
-
"description": "",
|
|
29
|
-
"displayName": "",
|
|
30
|
-
"inputs": [
|
|
31
|
-
{
|
|
32
|
-
"name": "sp_speed",
|
|
33
|
-
"type": "Float",
|
|
34
|
-
"displayName": ""
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
"name": "cmd_on",
|
|
38
|
-
"type": "Boolean",
|
|
39
|
-
"displayName": ""
|
|
40
|
-
}
|
|
41
|
-
],
|
|
42
|
-
"outputs": [
|
|
43
|
-
{
|
|
44
|
-
"name": "sts_spped",
|
|
45
|
-
"type": "Float",
|
|
46
|
-
"displayName": ""
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
"name": "sts_motor",
|
|
50
|
-
"type": "Boolean",
|
|
51
|
-
"displayName": ""
|
|
52
|
-
}
|
|
53
|
-
]
|
|
54
|
-
}
|
|
55
|
-
]
|
|
56
|
-
}
|
|
57
|
-
],
|
|
58
|
-
"variables": []
|
|
59
|
-
}
|
|
60
|
-
],
|
|
61
|
-
"objects": [],
|
|
62
|
-
"variables": []
|
|
63
|
-
}
|
|
64
|
-
]
|
|
65
|
-
}
|