@bldgblocks/node-red-contrib-control 0.1.33 → 0.1.36
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/nodes/accumulate-block.html +18 -8
- package/nodes/accumulate-block.js +39 -44
- package/nodes/add-block.html +1 -1
- package/nodes/add-block.js +18 -11
- package/nodes/alarm-collector.html +260 -0
- package/nodes/alarm-collector.js +292 -0
- package/nodes/alarm-config.html +129 -0
- package/nodes/alarm-config.js +126 -0
- package/nodes/alarm-service.html +96 -0
- package/nodes/alarm-service.js +142 -0
- package/nodes/analog-switch-block.js +25 -36
- package/nodes/and-block.js +44 -15
- package/nodes/average-block.js +46 -41
- package/nodes/boolean-switch-block.js +10 -28
- package/nodes/boolean-to-number-block.html +18 -5
- package/nodes/boolean-to-number-block.js +24 -16
- package/nodes/cache-block.js +24 -37
- package/nodes/call-status-block.html +91 -32
- package/nodes/call-status-block.js +398 -115
- package/nodes/changeover-block.html +5 -0
- package/nodes/changeover-block.js +167 -162
- package/nodes/comment-block.html +1 -1
- package/nodes/comment-block.js +14 -9
- package/nodes/compare-block.html +14 -4
- package/nodes/compare-block.js +23 -18
- package/nodes/contextual-label-block.html +5 -0
- package/nodes/contextual-label-block.js +6 -16
- package/nodes/convert-block.html +25 -39
- package/nodes/convert-block.js +31 -16
- package/nodes/count-block.html +11 -5
- package/nodes/count-block.js +34 -32
- package/nodes/delay-block.js +58 -53
- package/nodes/divide-block.js +43 -45
- package/nodes/edge-block.html +17 -10
- package/nodes/edge-block.js +43 -41
- package/nodes/enum-switch-block.js +6 -6
- package/nodes/frequency-block.html +6 -1
- package/nodes/frequency-block.js +64 -74
- package/nodes/global-getter.html +51 -15
- package/nodes/global-getter.js +74 -67
- package/nodes/global-setter.html +1 -1
- package/nodes/global-setter.js +168 -188
- package/nodes/history-buffer.html +96 -0
- package/nodes/history-buffer.js +461 -0
- package/nodes/history-collector.html +29 -1
- package/nodes/history-collector.js +37 -16
- package/nodes/history-config.html +13 -1
- package/nodes/history-service.html +84 -0
- package/nodes/history-service.js +52 -0
- package/nodes/hysteresis-block.html +5 -0
- package/nodes/hysteresis-block.js +13 -16
- package/nodes/interpolate-block.html +20 -2
- package/nodes/interpolate-block.js +39 -50
- package/nodes/join.html +78 -0
- package/nodes/join.js +78 -0
- package/nodes/latch-block.js +12 -14
- package/nodes/load-sequence-block.js +102 -110
- package/nodes/max-block.js +26 -26
- package/nodes/memory-block.js +57 -58
- package/nodes/min-block.js +26 -25
- package/nodes/minmax-block.js +35 -34
- package/nodes/modulo-block.js +45 -43
- package/nodes/multiply-block.js +43 -41
- package/nodes/negate-block.html +17 -7
- package/nodes/negate-block.js +25 -19
- package/nodes/network-point-read.html +128 -0
- package/nodes/network-point-read.js +230 -0
- package/nodes/{network-register.html → network-point-register.html} +94 -7
- package/nodes/network-point-register.js +126 -0
- package/nodes/network-point-write.html +149 -0
- package/nodes/network-point-write.js +222 -0
- package/nodes/network-service-bridge.html +131 -0
- package/nodes/network-service-bridge.js +376 -0
- package/nodes/network-service-read.html +81 -0
- package/nodes/network-service-read.js +58 -0
- package/nodes/{network-point-registry.html → network-service-registry.html} +19 -4
- package/nodes/{network-point-registry.js → network-service-registry.js} +7 -2
- package/nodes/network-service-write.html +89 -0
- package/nodes/network-service-write.js +83 -0
- package/nodes/nullify-block.js +13 -15
- package/nodes/on-change-block.html +17 -9
- package/nodes/on-change-block.js +49 -46
- package/nodes/oneshot-block.html +13 -10
- package/nodes/oneshot-block.js +57 -75
- package/nodes/or-block.js +44 -15
- package/nodes/pid-block.html +54 -4
- package/nodes/pid-block.js +459 -248
- package/nodes/priority-block.js +24 -35
- package/nodes/rate-limit-block.js +70 -72
- package/nodes/rate-of-change-block.html +33 -14
- package/nodes/rate-of-change-block.js +74 -62
- package/nodes/round-block.html +14 -9
- package/nodes/round-block.js +32 -25
- package/nodes/saw-tooth-wave-block.js +49 -76
- package/nodes/scale-range-block.html +12 -6
- package/nodes/scale-range-block.js +46 -39
- package/nodes/sine-wave-block.js +49 -57
- package/nodes/string-builder-block.js +6 -6
- package/nodes/subtract-block.js +38 -34
- package/nodes/thermistor-block.js +44 -44
- package/nodes/tick-tock-block.js +32 -32
- package/nodes/time-sequence-block.js +30 -42
- package/nodes/triangle-wave-block.js +49 -69
- package/nodes/tstat-block.js +34 -44
- package/nodes/units-block.html +90 -69
- package/nodes/units-block.js +22 -30
- package/nodes/utils.js +275 -3
- package/package.json +14 -6
- package/nodes/network-read.html +0 -56
- package/nodes/network-read.js +0 -59
- package/nodes/network-register.js +0 -161
- package/nodes/network-write.html +0 -64
- package/nodes/network-write.js +0 -126
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
module.exports = function(RED) {
|
|
2
|
-
function NetworkRegisterNode(config) {
|
|
3
|
-
RED.nodes.createNode(this, config);
|
|
4
|
-
const node = this;
|
|
5
|
-
|
|
6
|
-
// Config
|
|
7
|
-
node.registry = RED.nodes.getNode(config.registry);
|
|
8
|
-
node.pointId = parseInt(config.pointId);
|
|
9
|
-
node.writable = !!config.writable;
|
|
10
|
-
node.isRegistered = false;
|
|
11
|
-
|
|
12
|
-
// Initial Registration
|
|
13
|
-
if (node.registry && !isNaN(node.pointId)) {
|
|
14
|
-
const success = node.registry.register(node.pointId, {
|
|
15
|
-
nodeId: node.id, // for point registry collision checks
|
|
16
|
-
writable: node.writable,
|
|
17
|
-
path: "not ready",
|
|
18
|
-
store: "not ready"
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
if (success) {
|
|
22
|
-
node.isRegistered = true;
|
|
23
|
-
node.status({ fill: "blue", shape: "ring", text: `ID: ${node.pointId} (Waiting)` });
|
|
24
|
-
} else {
|
|
25
|
-
node.error(`Point ID ${node.pointId} is already in use.`);
|
|
26
|
-
node.status({ fill: "red", shape: "dot", text: "ID Conflict" });
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
node.on("input", function(msg, send, done) {
|
|
31
|
-
send = send || function() { node.send.apply(node, arguments); };
|
|
32
|
-
|
|
33
|
-
// Nothing to do. Return.
|
|
34
|
-
if (!node.isRegistered) {
|
|
35
|
-
node.status({ fill: "red", shape: "ring", text: `Not registered` });
|
|
36
|
-
if (done) done();
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
if (!msg || typeof msg !== "object") {
|
|
41
|
-
const message = `Invalid msg.`;
|
|
42
|
-
node.status({ fill: "red", shape: "ring", text: `${message}` });
|
|
43
|
-
if (done) done();
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (!node.registry) {
|
|
48
|
-
const message = `Registry not found. Create config node.`;
|
|
49
|
-
node.status({ fill: "red", shape: "ring", text: `${message}` });
|
|
50
|
-
msg.status = { status: "fail", pointId: node.pointId, error: `${message}` };
|
|
51
|
-
node.send(msg);
|
|
52
|
-
if (done) done();
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Message should contain data & metadata from a global setter node
|
|
57
|
-
const missingFields = [];
|
|
58
|
-
|
|
59
|
-
if (!msg.metadata) missingFields.push("metadata");
|
|
60
|
-
if (msg.value === undefined) missingFields.push("value");
|
|
61
|
-
if (msg.units === undefined) missingFields.push("units");
|
|
62
|
-
if (!msg.activePriority) missingFields.push("activePriority");
|
|
63
|
-
|
|
64
|
-
// Check nested metadata properties
|
|
65
|
-
if (msg.metadata) {
|
|
66
|
-
if (!msg.metadata.path) missingFields.push("metadata.path");
|
|
67
|
-
if (!msg.metadata.store) missingFields.push("metadata.store");
|
|
68
|
-
if (!msg.metadata.sourceId) missingFields.push("metadata.sourceId");
|
|
69
|
-
} else {
|
|
70
|
-
missingFields.push("metadata (entire object)");
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
if (missingFields.length > 0) {
|
|
74
|
-
const specificMessage = `Missing required fields: ${missingFields.join(', ')}`;
|
|
75
|
-
node.status({
|
|
76
|
-
fill: "red",
|
|
77
|
-
shape: "ring",
|
|
78
|
-
text: `${missingFields.length} missing: ${missingFields.slice(0, 3).join(', ')}${missingFields.length > 3 ? '...' : ''}`
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
node.send(msg);
|
|
82
|
-
if (done) done();
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
// Lookup current registration
|
|
88
|
-
let pointData = node.registry.lookup(node.pointId);
|
|
89
|
-
|
|
90
|
-
const incoming = {
|
|
91
|
-
writable: node.writable,
|
|
92
|
-
path: msg.metadata.path,
|
|
93
|
-
store: msg.metadata.store
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
// Update Registry on change
|
|
97
|
-
if (!pointData
|
|
98
|
-
|| pointData.nodeId !== node.nodeId
|
|
99
|
-
|| pointData.writable !== incoming.writable
|
|
100
|
-
|| pointData.path !== incoming.path
|
|
101
|
-
|| pointData.store !== incoming.store) {
|
|
102
|
-
|
|
103
|
-
node.registry.register(node.pointId, {
|
|
104
|
-
nodeId: node.id, // for point registry collision checks
|
|
105
|
-
writable: node.writable,
|
|
106
|
-
path: msg.metadata.path,
|
|
107
|
-
store: msg.metadata.store
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
pointData = node.registry.lookup(node.pointId);
|
|
111
|
-
|
|
112
|
-
let globalData = {};
|
|
113
|
-
globalData = node.context().global.get(pointData.path, pointData.store);
|
|
114
|
-
|
|
115
|
-
if (globalData === null || Object.keys(globalData).length === 0) {
|
|
116
|
-
const message = `Global data doesn't exist for (${pointData.store ?? "default"})::${pointData.path}::${node.pointId}`;
|
|
117
|
-
node.status({ fill: "red", shape: "ring", text: `${message}` });
|
|
118
|
-
msg.status = { status: "fail", pointId: node.pointId, error: `${message}` };
|
|
119
|
-
if (done) done();
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
let network = {
|
|
124
|
-
registry: node.registry.name,
|
|
125
|
-
pointId: node.pointId,
|
|
126
|
-
writable: node.writable
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const networkObject = { ...globalData, network: network};
|
|
130
|
-
const message = `Registered: (${pointData.store ?? "default"})::${pointData.path}::${node.pointId}`;
|
|
131
|
-
|
|
132
|
-
node.context().global.set(pointData.path, networkObject, pointData.store);
|
|
133
|
-
node.status({ fill: "blue", shape: "dot", text: `${message}` });
|
|
134
|
-
msg.status = { status: "success", pointId: node.pointId, error: `${message}` };
|
|
135
|
-
|
|
136
|
-
node.send(networkObject);
|
|
137
|
-
if (done) done();
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// Make it here, then message should match global and ready to go
|
|
142
|
-
// Pass through msg
|
|
143
|
-
const prefix = msg.activePriority === 'default' ? '' : 'P';
|
|
144
|
-
const message = `Passthrough: ${prefix}${msg.activePriority}:${msg.value}${msg.units}`;
|
|
145
|
-
node.status({ fill: "blue", shape: "ring", text: message });
|
|
146
|
-
|
|
147
|
-
node.send(msg);
|
|
148
|
-
if (done) done();
|
|
149
|
-
return;
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
// Cleanup
|
|
153
|
-
node.on('close', function(removed, done) {
|
|
154
|
-
if (removed && node.registry && node.isRegistered) {
|
|
155
|
-
node.registry.unregister(node.pointId, node.pointId);
|
|
156
|
-
}
|
|
157
|
-
done();
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
RED.nodes.registerType("network-register", NetworkRegisterNode);
|
|
161
|
-
}
|
package/nodes/network-write.html
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
<script type="text/html" data-template-name="network-write">
|
|
2
|
-
<div class="form-row">
|
|
3
|
-
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
4
|
-
<input type="text" id="node-input-name" placeholder="Name">
|
|
5
|
-
</div>
|
|
6
|
-
<div class="form-row">
|
|
7
|
-
<label for="node-input-registry"><i class="fa fa-book"></i> Registry</label>
|
|
8
|
-
<input type="text" id="node-input-registry">
|
|
9
|
-
</div>
|
|
10
|
-
|
|
11
|
-
<div class="form-tips">
|
|
12
|
-
<b>Input Payload Format:</b><br>
|
|
13
|
-
<pre>
|
|
14
|
-
{
|
|
15
|
-
"action": "write",
|
|
16
|
-
"pointId": 101,
|
|
17
|
-
"priority": 8,
|
|
18
|
-
"value": 75.5 // or null || "null" to release
|
|
19
|
-
}
|
|
20
|
-
</pre>
|
|
21
|
-
</div>
|
|
22
|
-
</script>
|
|
23
|
-
|
|
24
|
-
<script type="text/javascript">
|
|
25
|
-
RED.nodes.registerType('network-write', {
|
|
26
|
-
category: 'bldgblocks network',
|
|
27
|
-
color: '#3090C7',
|
|
28
|
-
defaults: {
|
|
29
|
-
name: { value: "" },
|
|
30
|
-
registry: { value: "", type: "network-point-registry", required: true }
|
|
31
|
-
},
|
|
32
|
-
inputs: 1,
|
|
33
|
-
outputs: 1,
|
|
34
|
-
icon: "font-awesome/fa-list-ol",
|
|
35
|
-
label: function() {
|
|
36
|
-
return this.name || "network write";
|
|
37
|
-
},
|
|
38
|
-
paletteLabel: "network write",
|
|
39
|
-
oneditprepare: function() {
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
});
|
|
43
|
-
</script>
|
|
44
|
-
|
|
45
|
-
<script type="text/markdown" data-help-name="network-write">
|
|
46
|
-
Writes network commands to Global Variables using the Priority Array logic.
|
|
47
|
-
|
|
48
|
-
### Input
|
|
49
|
-
: payload (object) : A command object containing
|
|
50
|
-
* `pointId` (number): The integer ID of the point.
|
|
51
|
-
* `priority` (number): The priority level (1-16) to write to.
|
|
52
|
-
* `value` (any): The value to set. Send `null` to relinquish (clear) this priority level.
|
|
53
|
-
|
|
54
|
-
### Output
|
|
55
|
-
: payload (object) : Confirmation object containing status, pointId, and the new calculated "Winner" value.
|
|
56
|
-
|
|
57
|
-
### Details
|
|
58
|
-
This node acts as the inbound gateway.
|
|
59
|
-
1. It looks up the `pointId` in the selected **Registry** to find the corresponding Global Variable path.
|
|
60
|
-
2. It fetches the current State Object.
|
|
61
|
-
3. It updates the specific slot in the `priority` array based on the command.
|
|
62
|
-
4. It recalculates the "Present Value" (highest priority active).
|
|
63
|
-
5. It saves the Global Variable and emits an update event, triggering any reactive Getters immediately.
|
|
64
|
-
</script>
|
package/nodes/network-write.js
DELETED
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
module.exports = function(RED) {
|
|
2
|
-
function NetworkWriteNode(config) {
|
|
3
|
-
RED.nodes.createNode(this, config);
|
|
4
|
-
const node = this;
|
|
5
|
-
|
|
6
|
-
node.registry = RED.nodes.getNode(config.registry);
|
|
7
|
-
|
|
8
|
-
node.on("input", function(msg, send, done) {
|
|
9
|
-
send = send || function() { node.send.apply(node, arguments); };
|
|
10
|
-
|
|
11
|
-
// Expecting: msg.payload = { pointId, priority, value }
|
|
12
|
-
if (!msg || !msg.pointId || !msg.priority || msg.value === undefined) {
|
|
13
|
-
node.status({ fill: "red", shape: "dot", text: "Invalid msg properties" });
|
|
14
|
-
msg.status = { status: "fail", pointId: msg.pointId, error: `Invalid msg properties` };
|
|
15
|
-
|
|
16
|
-
node.send(msg);
|
|
17
|
-
if (done) done();
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// Lookup Path
|
|
22
|
-
const entry = node.registry.lookup(msg.pointId);
|
|
23
|
-
const store = entry.store ?? "default";
|
|
24
|
-
const path = entry.path;
|
|
25
|
-
if (!entry || !path) {
|
|
26
|
-
node.status({ fill: "red", shape: "dot", text: `Unknown ID: (${store})::${path}::${msg.pointId}` });
|
|
27
|
-
msg.status = { status: "fail", pointId: msg.pointId, error: `Unknown ID: (${store})::${path}::${msg.pointId}` };
|
|
28
|
-
|
|
29
|
-
node.send(msg);
|
|
30
|
-
if (done) done();
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// Check Writable
|
|
35
|
-
if (!entry.writable) {
|
|
36
|
-
node.status({ fill: "red", shape: "dot", text: `Not Writable: (${store})::${path}::${msg.pointId}` });
|
|
37
|
-
msg.status = { status: "fail", pointId: msg.pointId, error: `Not Writable: (${store})::${path}::${msg.pointId}` };
|
|
38
|
-
|
|
39
|
-
node.send(msg);
|
|
40
|
-
if (done) done();
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Get State
|
|
45
|
-
const globalContext = node.context().global;
|
|
46
|
-
let state = globalContext.get(path, store);
|
|
47
|
-
|
|
48
|
-
if (!state || !state.priority) {
|
|
49
|
-
node.status({ fill: "red", shape: "ring", text: `Point Not Found: (${store})::${path}::${msg.pointId}` });
|
|
50
|
-
msg.status = { status: "fail", pointId: msg.pointId, error: `Point Not Found: (${store})::${path}::${msg.pointId}` };
|
|
51
|
-
|
|
52
|
-
node.send(msg);
|
|
53
|
-
if (done) done();
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Check Type
|
|
58
|
-
if (msg.value === "null" || msg.value === null) {
|
|
59
|
-
msg.value = null;
|
|
60
|
-
} else {
|
|
61
|
-
const inputType = typeof msg.value;
|
|
62
|
-
const dataType = state.metadata.type;
|
|
63
|
-
if (inputType !== dataType) {
|
|
64
|
-
node.status({ fill: "red", shape: "ring", text: `Mismatch type error: ${store}:${path} ID: ${msg.pointId}, ${inputType} !== ${dataType}` });
|
|
65
|
-
msg.status = { status: "fail", pointId: msg.pointId, error: `Mismatch type error: ${store}:${path} ID: ${msg.pointId}, ${inputType} !== ${dataType}` };
|
|
66
|
-
|
|
67
|
-
node.send(msg);
|
|
68
|
-
if (done) done();
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Update Priority
|
|
74
|
-
if (msg.priority === 'default') {
|
|
75
|
-
state.defaultValue = msg.value ?? state.defaultValue;
|
|
76
|
-
} else {
|
|
77
|
-
const priority = parseInt(msg.priority, 10);
|
|
78
|
-
if (isNaN(priority) || priority < 1 || priority > 16) {
|
|
79
|
-
node.status({ fill: "red", shape: "ring", text: `Invalid priority: ${msg.priority}` });
|
|
80
|
-
msg.status = { status: "fail", pointId: msg.pointId, error: `Invalid Priority: (${store})::${path}::${msg.pointId}` };
|
|
81
|
-
|
|
82
|
-
node.send(msg);
|
|
83
|
-
if (done) done();
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
state.priority[msg.priority] = msg.value;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Calculate Winner (Same logic as Setter)
|
|
91
|
-
let winnerValue = state.defaultValue;
|
|
92
|
-
let winnerPriority = 'default'
|
|
93
|
-
for (let i = 1; i <= 16; i++) {
|
|
94
|
-
if (state.priority[i] !== undefined && state.priority[i] !== null) {
|
|
95
|
-
winnerValue = state.priority[i];
|
|
96
|
-
winnerPriority = `${i}`
|
|
97
|
-
break;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
state.value = winnerValue;
|
|
101
|
-
state.activePriority = winnerPriority;
|
|
102
|
-
state.metadata.lastSet = new Date().toISOString();
|
|
103
|
-
|
|
104
|
-
// Save & Emit
|
|
105
|
-
const prefix1 = msg.priority === 'default' ? '' : 'P';
|
|
106
|
-
const prefix2 = state.activePriority === 'default' ? '' : 'P';
|
|
107
|
-
const message = `Wrote: ${prefix1}${msg.priority}:${msg.value} > (${store})::${path}::${msg.pointId} Active: ${prefix2}${winnerPriority}:${winnerValue}`;
|
|
108
|
-
node.status({ fill: "blue", shape: "ring", text: message });
|
|
109
|
-
|
|
110
|
-
globalContext.set(path, state, store);
|
|
111
|
-
msg = { ...state };
|
|
112
|
-
msg.status = { status: "ok", pointId: msg.pointId, message: message };
|
|
113
|
-
|
|
114
|
-
// Trigger global getters to update on new value
|
|
115
|
-
RED.events.emit("bldgblocks-global-update", {
|
|
116
|
-
key: path,
|
|
117
|
-
store: store,
|
|
118
|
-
data: state
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
node.send(msg);
|
|
122
|
-
if (done) done();
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
RED.nodes.registerType("network-write", NetworkWriteNode);
|
|
126
|
-
}
|