@bldgblocks/node-red-contrib-control 0.1.26 → 0.1.28
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/analog-switch-block.html +8 -9
- package/nodes/analog-switch-block.js +0 -23
- package/nodes/average-block.html +1 -1
- package/nodes/average-block.js +9 -9
- package/nodes/boolean-switch-block.html +2 -1
- package/nodes/boolean-switch-block.js +1 -2
- package/nodes/call-status-block.html +30 -45
- package/nodes/call-status-block.js +81 -195
- package/nodes/changeover-block.html +76 -12
- package/nodes/changeover-block.js +57 -28
- package/nodes/delay-block.html +1 -1
- package/nodes/delay-block.js +8 -8
- package/nodes/enum-switch-block.html +157 -0
- package/nodes/enum-switch-block.js +101 -0
- package/nodes/hysteresis-block.html +1 -1
- package/nodes/hysteresis-block.js +17 -13
- package/nodes/latch-block.html +55 -0
- package/nodes/latch-block.js +77 -0
- package/nodes/max-block.js +2 -4
- package/nodes/memory-block.js +3 -6
- package/nodes/min-block.js +2 -4
- package/nodes/minmax-block.js +9 -9
- package/nodes/on-change-block.html +0 -1
- package/nodes/on-change-block.js +4 -16
- package/nodes/pid-block.html +102 -80
- package/nodes/pid-block.js +121 -111
- package/nodes/rate-of-change-block.html +110 -0
- package/nodes/rate-of-change-block.js +233 -0
- package/nodes/string-builder-block.html +112 -0
- package/nodes/string-builder-block.js +89 -0
- package/nodes/tstat-block.html +85 -39
- package/nodes/tstat-block.js +105 -56
- package/nodes/utils.js +1 -22
- package/package.json +5 -1
|
@@ -7,15 +7,12 @@ module.exports = function(RED) {
|
|
|
7
7
|
node.name = config.name;
|
|
8
8
|
node.state = "within";
|
|
9
9
|
|
|
10
|
-
const typedProperties = ['upperLimit', 'lowerLimit', 'upperLimitThreshold', 'lowerLimitThreshold'];
|
|
11
|
-
|
|
12
10
|
// Evaluate typed-input properties
|
|
13
11
|
try {
|
|
14
|
-
|
|
15
|
-
node.
|
|
16
|
-
node.
|
|
17
|
-
node.
|
|
18
|
-
node.lowerLimitThreshold = parseFloat(evaluatedValues.lowerLimitThreshold);
|
|
12
|
+
node.upperLimit = parseFloat(RED.util.evaluateNodeProperty( config.upperLimit, config.upperLimitType, node ));
|
|
13
|
+
node.lowerLimit = parseFloat(RED.util.evaluateNodeProperty( config.lowerLimit, config.lowerLimitType, node ));
|
|
14
|
+
node.upperLimitThreshold = parseFloat(RED.util.evaluateNodeProperty( config.upperLimitThreshold, config.upperLimitThresholdType, node ));
|
|
15
|
+
node.lowerLimitThreshold = parseFloat(RED.util.evaluateNodeProperty( config.lowerLimitThreshold, config.lowerLimitThresholdType, node ));
|
|
19
16
|
} catch (err) {
|
|
20
17
|
node.error(`Error evaluating properties: ${err.message}`);
|
|
21
18
|
}
|
|
@@ -31,11 +28,18 @@ module.exports = function(RED) {
|
|
|
31
28
|
|
|
32
29
|
// Update typed-input properties if needed
|
|
33
30
|
try {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
31
|
+
if (utils.requiresEvaluation(config.upperLimitType)) {
|
|
32
|
+
node.upperLimit = parseFloat(RED.util.evaluateNodeProperty( config.upperLimit, config.upperLimitType, node, msg ));
|
|
33
|
+
}
|
|
34
|
+
if (utils.requiresEvaluation(config.lowerLimitType)) {
|
|
35
|
+
node.lowerLimit = parseFloat(RED.util.evaluateNodeProperty( config.lowerLimit, config.lowerLimitType, node, msg ));
|
|
36
|
+
}
|
|
37
|
+
if (utils.requiresEvaluation(config.upperLimitThresholdType)) {
|
|
38
|
+
node.upperLimitThreshold = parseFloat(RED.util.evaluateNodeProperty( config.upperLimitThreshold, config.upperLimitThresholdType, node, msg ));
|
|
39
|
+
}
|
|
40
|
+
if (utils.requiresEvaluation(config.lowerLimitThresholdType)) {
|
|
41
|
+
node.lowerLimitThreshold = parseFloat(RED.util.evaluateNodeProperty( config.lowerLimitThreshold, config.lowerLimitThresholdType, node, msg ));
|
|
42
|
+
}
|
|
39
43
|
} catch (err) {
|
|
40
44
|
node.error(`Error evaluating properties: ${err.message}`);
|
|
41
45
|
if (done) done();
|
|
@@ -80,7 +84,7 @@ module.exports = function(RED) {
|
|
|
80
84
|
|
|
81
85
|
// Add validation to ensure numbers
|
|
82
86
|
if (isNaN(upperTurnOn) || isNaN(upperTurnOff) || isNaN(lowerTurnOn) || isNaN(lowerTurnOff)) {
|
|
83
|
-
node.status({ fill: "red", shape: "ring", text: "invalid
|
|
87
|
+
node.status({ fill: "red", shape: "ring", text: "invalid limits calculation" });
|
|
84
88
|
if (done) done();
|
|
85
89
|
return;
|
|
86
90
|
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
<!-- UI Template Section -->
|
|
2
|
+
<script type="text/html" data-template-name="latch-block">
|
|
3
|
+
<div class="form-row">
|
|
4
|
+
<label for="node-input-name" title="Display name shown on the canvas"><i class="fa fa-tag"></i> Name</label>
|
|
5
|
+
<input type="text" id="node-input-name" placeholder="Name">
|
|
6
|
+
</div>
|
|
7
|
+
</script>
|
|
8
|
+
|
|
9
|
+
<!-- JavaScript Section -->
|
|
10
|
+
<script type="text/javascript">
|
|
11
|
+
RED.nodes.registerType("latch-block", {
|
|
12
|
+
category: "control",
|
|
13
|
+
color: "#301934",
|
|
14
|
+
defaults: {
|
|
15
|
+
name: { value: "" },
|
|
16
|
+
state: { value: false }
|
|
17
|
+
},
|
|
18
|
+
inputs: 1,
|
|
19
|
+
outputs: 1,
|
|
20
|
+
inputLabels: ["input"],
|
|
21
|
+
outputLabels: ["output"],
|
|
22
|
+
icon: "font-awesome/fa-toggle-on",
|
|
23
|
+
paletteLabel: "latch",
|
|
24
|
+
label: function() {
|
|
25
|
+
return this.name || "latch";
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
<!-- Help Section -->
|
|
31
|
+
<script type="text/markdown" data-help-name="latch-block">
|
|
32
|
+
Latch output based on control messages.
|
|
33
|
+
|
|
34
|
+
### Inputs
|
|
35
|
+
: context (string) : Configuration commands (`set`, `reset`).
|
|
36
|
+
: payload (boolean) : `true` to set latch, `false` to reset latch when paired with appropriate `context`.
|
|
37
|
+
|
|
38
|
+
### Outputs
|
|
39
|
+
: output (msg) : `msg.payload` `true` or `false` based on latch state.
|
|
40
|
+
|
|
41
|
+
### Details
|
|
42
|
+
Set or reset the latch state based on input messages. `msg.context` = `"set"` with `msg.payload` = `true`
|
|
43
|
+
sets the latch `true`, while `msg.context` = `"reset"` with `msg.payload` = `true` resets it to `false`.
|
|
44
|
+
|
|
45
|
+
### Status
|
|
46
|
+
- Green (dot): Configuration update
|
|
47
|
+
- Blue (dot): State changed
|
|
48
|
+
- Blue (ring): State unchanged
|
|
49
|
+
- Red (ring): Error
|
|
50
|
+
- Yellow (ring): Warning
|
|
51
|
+
|
|
52
|
+
### References
|
|
53
|
+
- [Node-RED Documentation](https://nodered.org/docs/)
|
|
54
|
+
- [GitHub Repository](https://github.com/BldgBlocks/node-red-contrib-buildingblocks-control.git)
|
|
55
|
+
</script>
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
module.exports = function(RED) {
|
|
2
|
+
function LatchBlockNode(config) {
|
|
3
|
+
RED.nodes.createNode(this, config);
|
|
4
|
+
|
|
5
|
+
const node = this;
|
|
6
|
+
|
|
7
|
+
// Initialize state from config
|
|
8
|
+
node.state = config.state;
|
|
9
|
+
|
|
10
|
+
// Set initial status
|
|
11
|
+
node.status({
|
|
12
|
+
fill: "green",
|
|
13
|
+
shape: "dot",
|
|
14
|
+
text: `state: ${node.state}`
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
node.on("input", function(msg, send, done) {
|
|
18
|
+
send = send || function() { node.send.apply(node, arguments); };
|
|
19
|
+
|
|
20
|
+
// Guard against invalid message
|
|
21
|
+
if (!msg) {
|
|
22
|
+
node.status({ fill: "red", shape: "ring", text: "invalid message" });
|
|
23
|
+
if (done) done();
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Validate context
|
|
28
|
+
if (!msg.hasOwnProperty("context") || typeof msg.context !== "string") {
|
|
29
|
+
node.status({ fill: "red", shape: "ring", text: "missing or invalid context" });
|
|
30
|
+
if (done) done();
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Handle context commands
|
|
35
|
+
switch (msg.context) {
|
|
36
|
+
case "set":
|
|
37
|
+
if (node.state) {
|
|
38
|
+
node.status({ fill: "blue", shape: "ring", text: `state: ${node.state}` });
|
|
39
|
+
} else {
|
|
40
|
+
if (msg.payload) {
|
|
41
|
+
node.state = true;
|
|
42
|
+
node.status({ fill: "blue", shape: "dot", text: `state: ${node.state}` });
|
|
43
|
+
} else {
|
|
44
|
+
node.status({ fill: "blue", shape: "ring", text: `state: ${node.state}` });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// Output latch value regardless
|
|
48
|
+
send({ payload: node.state });
|
|
49
|
+
break;
|
|
50
|
+
case "reset":
|
|
51
|
+
if (node.state === false) {
|
|
52
|
+
node.status({ fill: "blue", shape: "ring", text: `state: ${node.state}` });
|
|
53
|
+
} else {
|
|
54
|
+
if (msg.payload) {
|
|
55
|
+
node.state = false;
|
|
56
|
+
node.status({ fill: "blue", shape: "dot", text: `state: ${node.state}` });
|
|
57
|
+
} else {
|
|
58
|
+
node.status({ fill: "blue", shape: "ring", text: `state: ${node.state}` });
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
send({ payload: node.state });
|
|
62
|
+
break;
|
|
63
|
+
default:
|
|
64
|
+
node.status({ fill: "yellow", shape: "ring", text: "unknown context" });
|
|
65
|
+
if (done) done("Unknown context");
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (done) done();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
node.on("close", function(done) {
|
|
72
|
+
done();
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
RED.nodes.registerType("latch-block", LatchBlockNode);
|
|
77
|
+
};
|
package/nodes/max-block.js
CHANGED
|
@@ -12,8 +12,7 @@ module.exports = function(RED) {
|
|
|
12
12
|
|
|
13
13
|
// Evaluate typed-inputs
|
|
14
14
|
try {
|
|
15
|
-
node.runtime.max = RED.util.evaluateNodeProperty( config.max, config.maxType, node );
|
|
16
|
-
node.runtime.max = parseFloat(node.runtime.max);
|
|
15
|
+
node.runtime.max = parseFloat( RED.util.evaluateNodeProperty( config.max, config.maxType, node ));
|
|
17
16
|
} catch(err) {
|
|
18
17
|
node.status({ fill: "red", shape: "ring", text: "error evaluating properties" });
|
|
19
18
|
}
|
|
@@ -34,8 +33,7 @@ module.exports = function(RED) {
|
|
|
34
33
|
// Evaluate typed-inputs if needed
|
|
35
34
|
try {
|
|
36
35
|
if (utils.requiresEvaluation(config.maxType)) {
|
|
37
|
-
node.runtime.max = RED.util.evaluateNodeProperty( config.max, config.maxType, node, msg );
|
|
38
|
-
node.runtime.max = parseFloat(node.runtime.max);
|
|
36
|
+
node.runtime.max = parseFloat( RED.util.evaluateNodeProperty( config.max, config.maxType, node, msg ));
|
|
39
37
|
}
|
|
40
38
|
} catch(err) {
|
|
41
39
|
node.status({ fill: "red", shape: "ring", text: "error evaluating properties" });
|
package/nodes/memory-block.js
CHANGED
|
@@ -13,15 +13,13 @@ module.exports = function(RED) {
|
|
|
13
13
|
node.runtime = {
|
|
14
14
|
name: config.name,
|
|
15
15
|
writePeriod: config.writePeriod,
|
|
16
|
-
writePeriodType: config.writePeriodType,
|
|
17
16
|
transferProperty: config.transferProperty,
|
|
18
17
|
writeOnUpdate: config.writeOnUpdate === true,
|
|
19
18
|
storedMsg: null
|
|
20
19
|
};
|
|
21
20
|
|
|
22
21
|
// Resolve typed inputs
|
|
23
|
-
node.runtime.writePeriod = RED.util.evaluateNodeProperty( config.writePeriod, config.writePeriodType, node );
|
|
24
|
-
node.runtime.writePeriod = parseFloat(node.runtime.writePeriod);
|
|
22
|
+
node.runtime.writePeriod = parseFloat(RED.util.evaluateNodeProperty( config.writePeriod, config.writePeriodType, node ));
|
|
25
23
|
|
|
26
24
|
// File path for persistent storage
|
|
27
25
|
const filePath = path.join(RED.settings.userDir, `memory-${node.id}.json`);
|
|
@@ -88,9 +86,8 @@ module.exports = function(RED) {
|
|
|
88
86
|
}
|
|
89
87
|
|
|
90
88
|
// Evaluate typed-inputs if needed
|
|
91
|
-
if (utils.requiresEvaluation(
|
|
92
|
-
node.runtime.writePeriod = RED.util.evaluateNodeProperty( config.writePeriod, config.writePeriodType, node, msg );
|
|
93
|
-
node.runtime.writePeriod = parseFloat(node.runtime.writePeriod);
|
|
89
|
+
if (utils.requiresEvaluation(config.writePeriodType)) {
|
|
90
|
+
node.runtime.writePeriod = parseFloat(RED.util.evaluateNodeProperty( config.writePeriod, config.writePeriodType, node, msg ));
|
|
94
91
|
}
|
|
95
92
|
|
|
96
93
|
// Initialize output array: [Output 1, Output 2]
|
package/nodes/min-block.js
CHANGED
|
@@ -12,8 +12,7 @@ module.exports = function(RED) {
|
|
|
12
12
|
|
|
13
13
|
// Evaluate typed-inputs
|
|
14
14
|
try {
|
|
15
|
-
node.runtime.min = RED.util.evaluateNodeProperty( config.min, config.minType, node );
|
|
16
|
-
node.runtime.min = parseFloat(node.runtime.min);
|
|
15
|
+
node.runtime.min = parseFloat(RED.util.evaluateNodeProperty( config.min, config.minType, node ));
|
|
17
16
|
} catch(err) {
|
|
18
17
|
node.status({ fill: "red", shape: "ring", text: "error evaluating properties" });
|
|
19
18
|
}
|
|
@@ -34,8 +33,7 @@ module.exports = function(RED) {
|
|
|
34
33
|
// Evaluate typed-inputs if needed
|
|
35
34
|
try {
|
|
36
35
|
if (utils.requiresEvaluation(config.minType)) {
|
|
37
|
-
node.runtime.min = RED.util.evaluateNodeProperty( config.min, config.minType, node, msg );
|
|
38
|
-
node.runtime.min = parseFloat(node.runtime.min);
|
|
36
|
+
node.runtime.min = parseFloat(RED.util.evaluateNodeProperty( config.min, config.minType, node, msg ));
|
|
39
37
|
}
|
|
40
38
|
} catch(err) {
|
|
41
39
|
node.status({ fill: "red", shape: "ring", text: "error evaluating properties" });
|
package/nodes/minmax-block.js
CHANGED
|
@@ -9,14 +9,11 @@ module.exports = function(RED) {
|
|
|
9
9
|
node.runtime = {
|
|
10
10
|
name: config.name,
|
|
11
11
|
};
|
|
12
|
-
|
|
13
|
-
const typedProperties = ['min', 'max'];
|
|
14
12
|
|
|
15
13
|
// Evaluate typed-input properties
|
|
16
14
|
try {
|
|
17
|
-
|
|
18
|
-
node.runtime.
|
|
19
|
-
node.runtime.max = parseFloat(evaluatedValues.max);
|
|
15
|
+
node.runtime.min = parseFloat(RED.util.evaluateNodeProperty( config.min, config.minType, node ));
|
|
16
|
+
node.runtime.max = parseFloat(RED.util.evaluateNodeProperty( config.max, config.maxType, node ));
|
|
20
17
|
} catch (err) {
|
|
21
18
|
node.error(`Error evaluating properties: ${err.message}`);
|
|
22
19
|
}
|
|
@@ -35,10 +32,13 @@ module.exports = function(RED) {
|
|
|
35
32
|
}
|
|
36
33
|
|
|
37
34
|
// Update typed-input properties if needed
|
|
38
|
-
try {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
35
|
+
try {
|
|
36
|
+
if (utils.requiresEvaluation(config.minType)) {
|
|
37
|
+
node.runtime.min = parseFloat(RED.util.evaluateNodeProperty( config.min, config.minType, node, msg ));
|
|
38
|
+
}
|
|
39
|
+
if (utils.requiresEvaluation(config.maxType)) {
|
|
40
|
+
node.runtime.max = parseFloat(RED.util.evaluateNodeProperty( config.max, config.maxType, node, msg ));
|
|
41
|
+
}
|
|
42
42
|
} catch (err) {
|
|
43
43
|
node.error(`Error evaluating properties: ${err.message}`);
|
|
44
44
|
if (done) done();
|
|
@@ -64,7 +64,6 @@ differs from the last output value. When `period > 0`, outputs the first message
|
|
|
64
64
|
Supports complex payloads (objects, arrays) via deep comparison.
|
|
65
65
|
Configuration
|
|
66
66
|
- `msg.context = "period"` Sets period (ms), no output.
|
|
67
|
-
- `msg.context = "status"` Outputs `{ period, periodType }`.
|
|
68
67
|
|
|
69
68
|
### Status
|
|
70
69
|
- Green (dot): Configuration update
|
package/nodes/on-change-block.js
CHANGED
|
@@ -14,8 +14,7 @@ module.exports = function(RED) {
|
|
|
14
14
|
|
|
15
15
|
// Evaluate typed-input properties
|
|
16
16
|
try {
|
|
17
|
-
node.runtime.period = RED.util.evaluateNodeProperty( config.period, config.periodType, node );
|
|
18
|
-
node.runtime.period = parseFloat(node.runtime.period);
|
|
17
|
+
node.runtime.period = parseFloat(RED.util.evaluateNodeProperty( config.period, config.periodType, node ));
|
|
19
18
|
} catch (err) {
|
|
20
19
|
node.status({ fill: "red", shape: "ring", text: "error evaluating properties" });
|
|
21
20
|
}
|
|
@@ -33,8 +32,7 @@ module.exports = function(RED) {
|
|
|
33
32
|
// Evaluate typed-input properties if needed
|
|
34
33
|
try {
|
|
35
34
|
if (utils.requiresEvaluation(node.runtime.periodType)) {
|
|
36
|
-
node.runtime.period = RED.util.evaluateNodeProperty(
|
|
37
|
-
node.runtime.period = parseFloat(node.runtime.period);
|
|
35
|
+
node.runtime.period = parseFloat(RED.util.evaluateNodeProperty( config.period, config.periodType, node, msg ));
|
|
38
36
|
}
|
|
39
37
|
} catch (err) {
|
|
40
38
|
node.status({ fill: "red", shape: "ring", text: "error evaluating properties" });
|
|
@@ -72,16 +70,6 @@ module.exports = function(RED) {
|
|
|
72
70
|
if (done) done();
|
|
73
71
|
return;
|
|
74
72
|
}
|
|
75
|
-
if (msg.context === "status") {
|
|
76
|
-
send({
|
|
77
|
-
payload: {
|
|
78
|
-
period: node.runtime.period,
|
|
79
|
-
periodType: node.runtime.periodType
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
if (done) done();
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
73
|
// Ignore unknown context
|
|
86
74
|
}
|
|
87
75
|
|
|
@@ -117,7 +105,7 @@ module.exports = function(RED) {
|
|
|
117
105
|
node.status({
|
|
118
106
|
fill: "blue",
|
|
119
107
|
shape: "ring",
|
|
120
|
-
text: `filtered: ${JSON.stringify(currentValue).slice(0, 20)}
|
|
108
|
+
text: `filtered: ${JSON.stringify(currentValue).slice(0, 20)} |`
|
|
121
109
|
});
|
|
122
110
|
if (done) done();
|
|
123
111
|
return;
|
|
@@ -141,7 +129,7 @@ module.exports = function(RED) {
|
|
|
141
129
|
node.status({
|
|
142
130
|
fill: "blue",
|
|
143
131
|
shape: "ring",
|
|
144
|
-
text: `
|
|
132
|
+
text: `filtered: ${JSON.stringify(currentValue).slice(0, 20)}` // remove ' |' to indicate end of filter period
|
|
145
133
|
});
|
|
146
134
|
}, node.runtime.period);
|
|
147
135
|
}
|
package/nodes/pid-block.html
CHANGED
|
@@ -6,23 +6,28 @@
|
|
|
6
6
|
</div>
|
|
7
7
|
<div class="form-row">
|
|
8
8
|
<label for="node-input-kp" title="Proportional gain (number)"><i class="fa fa-sliders"></i> Kp</label>
|
|
9
|
-
<input type="
|
|
9
|
+
<input type="text" id="node-input-kp" placeholder="0" step="any">
|
|
10
|
+
<input type="hidden" id="node-input-kpType">
|
|
10
11
|
</div>
|
|
11
12
|
<div class="form-row">
|
|
12
13
|
<label for="node-input-ki" title="Integral gain (number)"><i class="fa fa-sliders"></i> Ki</label>
|
|
13
|
-
<input type="
|
|
14
|
+
<input type="text" id="node-input-ki" placeholder="0" step="any">
|
|
15
|
+
<input type="hidden" id="node-input-kiType">
|
|
14
16
|
</div>
|
|
15
17
|
<div class="form-row">
|
|
16
18
|
<label for="node-input-kd" title="Derivative gain (number)"><i class="fa fa-sliders"></i> Kd</label>
|
|
17
|
-
<input type="
|
|
19
|
+
<input type="text" id="node-input-kd" placeholder="0" step="any">
|
|
20
|
+
<input type="hidden" id="node-input-kdType">
|
|
18
21
|
</div>
|
|
19
22
|
<div class="form-row">
|
|
20
23
|
<label for="node-input-setpoint" title="Target setpoint (number)"><i class="fa fa-crosshairs"></i> Setpoint</label>
|
|
21
|
-
<input type="
|
|
24
|
+
<input type="text" id="node-input-setpoint" placeholder="0" step="any">
|
|
25
|
+
<input type="hidden" id="node-input-setpointType">
|
|
22
26
|
</div>
|
|
23
27
|
<div class="form-row">
|
|
24
28
|
<label for="node-input-deadband" title="Deadband range around setpoint (non-negative number)"><i class="fa fa-arrows-h"></i> Deadband</label>
|
|
25
|
-
<input type="
|
|
29
|
+
<input type="text" id="node-input-deadband" placeholder="0" step="any" min="0">
|
|
30
|
+
<input type="hidden" id="node-input-deadbandType">
|
|
26
31
|
</div>
|
|
27
32
|
<div class="form-row">
|
|
28
33
|
<label for="node-input-dbBehavior" title="Deadband behavior: ReturnToZero or HoldLastResult"><i class="fa fa-cog"></i> Deadband Behavior</label>
|
|
@@ -33,15 +38,18 @@
|
|
|
33
38
|
</div>
|
|
34
39
|
<div class="form-row">
|
|
35
40
|
<label for="node-input-outMin" title="Minimum output limit (number, less than outMax, leave empty for no limit)"><i class="fa fa-arrow-down"></i> Out Min</label>
|
|
36
|
-
<input type="
|
|
41
|
+
<input type="text" id="node-input-outMin" placeholder="No min" step="any">
|
|
42
|
+
<input type="hidden" id="node-input-outMinType">
|
|
37
43
|
</div>
|
|
38
44
|
<div class="form-row">
|
|
39
45
|
<label for="node-input-outMax" title="Maximum output limit (number, greater than outMin, leave empty for no limit)"><i class="fa fa-arrow-up"></i> Out Max</label>
|
|
40
|
-
<input type="
|
|
46
|
+
<input type="text" id="node-input-outMax" placeholder="No max" step="any">
|
|
47
|
+
<input type="hidden" id="node-input-outMaxType">
|
|
41
48
|
</div>
|
|
42
49
|
<div class="form-row">
|
|
43
50
|
<label for="node-input-maxChange" title="Maximum output change per cycle (non-negative number)"><i class="fa fa-exchange"></i> Max Change</label>
|
|
44
|
-
<input type="
|
|
51
|
+
<input type="text" id="node-input-maxChange" placeholder="0" step="any" min="0">
|
|
52
|
+
<input type="hidden" id="node-input-maxChangeType">
|
|
45
53
|
</div>
|
|
46
54
|
<div class="form-row">
|
|
47
55
|
<label for="node-input-directAction" title="Direct (true) or reverse (false) action"><i class="fa fa-exchange"></i> Direct Action</label>
|
|
@@ -49,11 +57,8 @@
|
|
|
49
57
|
</div>
|
|
50
58
|
<div class="form-row">
|
|
51
59
|
<label for="node-input-run" title="Enable (true) or disable (false) PID calculation"><i class="fa fa-play"></i> Run</label>
|
|
52
|
-
<input type="
|
|
53
|
-
|
|
54
|
-
<div class="form-row">
|
|
55
|
-
<label><i class="fa fa-info-circle"></i> Changed Runtime Values</label>
|
|
56
|
-
<pre id="node-runtime-changes" style="color: #555; white-space: pre-wrap;">Changed Values: None</pre>
|
|
60
|
+
<input type="text" id="node-input-run" style="width: auto; vertical-align: middle;" checked>
|
|
61
|
+
<input type="hidden" id="node-input-runType">
|
|
57
62
|
</div>
|
|
58
63
|
</script>
|
|
59
64
|
|
|
@@ -64,17 +69,26 @@
|
|
|
64
69
|
color: "#301934",
|
|
65
70
|
defaults: {
|
|
66
71
|
name: { value: "" },
|
|
67
|
-
kp: { value: 0, required: true
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
+
kp: { value: 0, required: true },
|
|
73
|
+
kpType: { value: "num" },
|
|
74
|
+
ki: { value: 0, required: true },
|
|
75
|
+
kiType: { value: "num" },
|
|
76
|
+
kd: { value: 0, required: true },
|
|
77
|
+
kdType: { value: "num" },
|
|
78
|
+
setpoint: { value: 0, required: true },
|
|
79
|
+
setpointType: { value: "num" },
|
|
80
|
+
deadband: { value: 0, required: true },
|
|
81
|
+
deadbandType: { value: "num" },
|
|
72
82
|
dbBehavior: { value: "ReturnToZero" },
|
|
73
|
-
outMin: { value: null
|
|
74
|
-
|
|
75
|
-
|
|
83
|
+
outMin: { value: null },
|
|
84
|
+
outMinType: { value: "num" },
|
|
85
|
+
outMax: { value: null },
|
|
86
|
+
outMaxType: { value: "num" },
|
|
87
|
+
maxChange: { value: 0, required: true },
|
|
88
|
+
maxChangeType: { value: "num" },
|
|
76
89
|
directAction: { value: false },
|
|
77
|
-
run: { value: true }
|
|
90
|
+
run: { value: true },
|
|
91
|
+
runType: { value: "bool" }
|
|
78
92
|
},
|
|
79
93
|
inputs: 1,
|
|
80
94
|
outputs: 1,
|
|
@@ -87,59 +101,67 @@
|
|
|
87
101
|
},
|
|
88
102
|
oneditprepare: function() {
|
|
89
103
|
const node = this;
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
$("#node-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
// Initialize typed inputs
|
|
107
|
+
$("#node-input-kp").typedInput({
|
|
108
|
+
default: "num",
|
|
109
|
+
types: ["num", "msg", "flow", "global"],
|
|
110
|
+
typeField: "#node-input-kpType"
|
|
111
|
+
}).typedInput("type", node.kpType || "num").typedInput("value", node.kp);
|
|
112
|
+
|
|
113
|
+
$("#node-input-ki").typedInput({
|
|
114
|
+
default: "num",
|
|
115
|
+
types: ["num", "msg", "flow", "global"],
|
|
116
|
+
typeField: "#node-input-kiType"
|
|
117
|
+
}).typedInput("type", node.kiType || "num").typedInput("value", node.ki);
|
|
118
|
+
|
|
119
|
+
$("#node-input-kd").typedInput({
|
|
120
|
+
default: "num",
|
|
121
|
+
types: ["num", "msg", "flow", "global"],
|
|
122
|
+
typeField: "#node-input-kdType"
|
|
123
|
+
}).typedInput("type", node.kdType || "num").typedInput("value", node.kd);
|
|
124
|
+
|
|
125
|
+
$("#node-input-setpoint").typedInput({
|
|
126
|
+
default: "num",
|
|
127
|
+
types: ["num", "msg", "flow", "global"],
|
|
128
|
+
typeField: "#node-input-setpointType"
|
|
129
|
+
}).typedInput("type", node.setpointType || "num").typedInput("value", node.setpoint);
|
|
130
|
+
|
|
131
|
+
$("#node-input-deadband").typedInput({
|
|
132
|
+
default: "num",
|
|
133
|
+
types: ["num", "msg", "flow", "global"],
|
|
134
|
+
typeField: "#node-input-deadbandType"
|
|
135
|
+
}).typedInput("type", node.deadbandType || "num").typedInput("value", node.deadband);
|
|
136
|
+
|
|
137
|
+
$("#node-input-outMin").typedInput({
|
|
138
|
+
default: "num",
|
|
139
|
+
types: ["num", "msg", "flow", "global"],
|
|
140
|
+
typeField: "#node-input-outMinType"
|
|
141
|
+
}).typedInput("type", node.outMinType || "num").typedInput("value", node.outMin);
|
|
142
|
+
|
|
143
|
+
$("#node-input-outMax").typedInput({
|
|
144
|
+
default: "num",
|
|
145
|
+
types: ["num", "msg", "flow", "global"],
|
|
146
|
+
typeField: "#node-input-outMaxType"
|
|
147
|
+
}).typedInput("type", node.outMaxType || "num").typedInput("value", node.outMax);
|
|
148
|
+
|
|
149
|
+
$("#node-input-maxChange").typedInput({
|
|
150
|
+
default: "num",
|
|
151
|
+
types: ["num", "msg", "flow", "global"],
|
|
152
|
+
typeField: "#node-input-maxChangeType"
|
|
153
|
+
}).typedInput("type", node.maxChangeType || "num").typedInput("value", node.maxChange);
|
|
154
|
+
|
|
155
|
+
$("#node-input-run").typedInput({
|
|
156
|
+
default: "bool",
|
|
157
|
+
types: ["bool", "msg", "flow", "global"],
|
|
158
|
+
typeField: "#node-input-runType"
|
|
159
|
+
}).typedInput("type", node.runType || "bool").typedInput("value", node.run);
|
|
160
|
+
|
|
161
|
+
} catch (err) {
|
|
162
|
+
console.error("Error in oneditprepare:", err);
|
|
163
|
+
}
|
|
164
|
+
|
|
143
165
|
}
|
|
144
166
|
});
|
|
145
167
|
</script>
|
|
@@ -193,11 +215,11 @@ Ziegler-Nichols tuning sets `kp = 0.6*Ku`, `ki = 2*kp/Tu`, `kd = kp*Tu/8` after
|
|
|
193
215
|
- Invalid config at startup: Red status (`invalid config` or specific), resets to defaults.
|
|
194
216
|
|
|
195
217
|
### Status
|
|
196
|
-
- Green (dot): Configuration, reset, or tuning
|
|
197
|
-
- Blue (dot): Output change
|
|
198
|
-
- Blue (ring): Output unchanged
|
|
199
|
-
- Red (ring): Errors
|
|
200
|
-
- Yellow (ring): Unknown context
|
|
218
|
+
- Green (dot): Configuration, reset, or tuning
|
|
219
|
+
- Blue (dot): Output change
|
|
220
|
+
- Blue (ring): Output unchanged
|
|
221
|
+
- Red (ring): Errors
|
|
222
|
+
- Yellow (ring): Unknown context
|
|
201
223
|
|
|
202
224
|
### References
|
|
203
225
|
- [Node-RED Documentation](https://nodered.org/docs/)
|