@mschaeffler/node-red-logic 1.1.0 → 1.2.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/math_mean.html +97 -0
- package/math_mean.js +77 -0
- package/math_statistics.html +1 -8
- package/math_statistics.js +19 -40
- package/package.json +3 -2
package/math_mean.html
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
<script type="text/javascript">
|
|
2
|
+
RED.nodes.registerType('mean',{
|
|
3
|
+
category: 'function',
|
|
4
|
+
color: "#C0DEED",
|
|
5
|
+
defaults: {
|
|
6
|
+
name:{value:""},
|
|
7
|
+
property:{value:"payload", required:true},
|
|
8
|
+
deltaTime:{value:"60", required:true, validate:function(v){
|
|
9
|
+
return v!=='' && !isNaN(v) && Number(v)>0;}
|
|
10
|
+
},
|
|
11
|
+
minData:{value:"1", required:true, validate:function(v){
|
|
12
|
+
return v!=='' && !isNaN(v) && Number(v)>0;}
|
|
13
|
+
},
|
|
14
|
+
filter:{value:"", required:true}
|
|
15
|
+
},
|
|
16
|
+
inputs:1,
|
|
17
|
+
outputs:1,
|
|
18
|
+
icon:"font-awesome/fa-arrows-v",
|
|
19
|
+
label: function() {
|
|
20
|
+
return this.name||"mean";
|
|
21
|
+
},
|
|
22
|
+
labelStyle: function() {
|
|
23
|
+
return this.name?"node_label_italic":"";
|
|
24
|
+
},
|
|
25
|
+
inputLabels: 'input',
|
|
26
|
+
outputLabels: 'output',
|
|
27
|
+
oneditprepare: function() {
|
|
28
|
+
$("#node-input-property").typedInput({
|
|
29
|
+
type:"msg",
|
|
30
|
+
types:["msg"],
|
|
31
|
+
typeField: "#node-input-property-type"
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
</script>
|
|
36
|
+
|
|
37
|
+
<script type="text/html" data-template-name="mean">
|
|
38
|
+
<div class="form-row">
|
|
39
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
40
|
+
<input type="text" id="node-input-name" placeholder="Name">
|
|
41
|
+
</div>
|
|
42
|
+
<div class="form-row">
|
|
43
|
+
<label for="node-input-input"><i class="fa fa-ellipsis-h"></i> Eigenschaft</label>
|
|
44
|
+
<input type="text" id="node-input-property">
|
|
45
|
+
<input type="hidden" id="node-input-property-type">
|
|
46
|
+
</div>
|
|
47
|
+
<div class="form-row">
|
|
48
|
+
<label for="node-input-minMean"><i class="fa fa-hourglass-o"></i> Zeitspanne</label>
|
|
49
|
+
<input type="text" id="node-input-deltaTime">
|
|
50
|
+
</div>
|
|
51
|
+
<div class="form-row">
|
|
52
|
+
<label for="node-input-minData"><i class="fa fa-check-square-o "></i> min. Data</label>
|
|
53
|
+
<input type="text" id="node-input-minData">
|
|
54
|
+
</div>
|
|
55
|
+
<div class="form-row">
|
|
56
|
+
<label for="node-input-filter"><i class="fa fa-filter "></i> Filterzeit</label>
|
|
57
|
+
<input type="text" id="node-input-filter">
|
|
58
|
+
</div>
|
|
59
|
+
</script>
|
|
60
|
+
|
|
61
|
+
<script type="text/html" data-help-name="mean">
|
|
62
|
+
|
|
63
|
+
<p>Calculate mean value of one or more data streams.</p>
|
|
64
|
+
<p>If the msg property <code>invalid</code> is present in the message, all nodes ignore the message.</p>
|
|
65
|
+
|
|
66
|
+
<h3>Input</h3>
|
|
67
|
+
|
|
68
|
+
<p>The different inputs are differentiated by topics.</p>
|
|
69
|
+
<p>The message property to be used as payload can be defined with the <code>Property</code> property.</p>
|
|
70
|
+
|
|
71
|
+
<dl class="message-properties">
|
|
72
|
+
<dt>topic <span class="property-type">string</span></dt>
|
|
73
|
+
<dd> name of the input channel.</dd>
|
|
74
|
+
<dt>payload <span class="property-type">number</span></dt>
|
|
75
|
+
<dd> input value for <code>topic</code>.</dd>
|
|
76
|
+
</dl>
|
|
77
|
+
|
|
78
|
+
<h3>Output</h3>
|
|
79
|
+
|
|
80
|
+
<dl class="message-properties">
|
|
81
|
+
<dt>payload <span class="property-type">number</span></dt>
|
|
82
|
+
<dd> average value.</dd>
|
|
83
|
+
</dl>
|
|
84
|
+
|
|
85
|
+
<h3>Parameter</h3>
|
|
86
|
+
|
|
87
|
+
<dl class="message-properties">
|
|
88
|
+
<dt>Property <span class="property-type">string</span></dt>
|
|
89
|
+
<dd> defines the message property to be used as payload.</dd>
|
|
90
|
+
<dt>Delta time <span class="property-type">number</span></dt>
|
|
91
|
+
<dd> time frame to collect the data for the analysis.</dd>
|
|
92
|
+
<dt>min. Data <span class="property-type">number</span></dt>
|
|
93
|
+
<dd> min. amount of data points to generate an output.</dd>
|
|
94
|
+
<dt>Filterzeit <span class="property-type">number</span></dt>
|
|
95
|
+
<dd> send output only after this time.</dd>
|
|
96
|
+
</dl>
|
|
97
|
+
</script>
|
package/math_mean.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
module.exports = function(RED) {
|
|
2
|
+
|
|
3
|
+
function MeanNode(config) {
|
|
4
|
+
RED.nodes.createNode(this,config);
|
|
5
|
+
//this.config = config;
|
|
6
|
+
var node = this;
|
|
7
|
+
var context = this.context();
|
|
8
|
+
this.property = config.property || "payload";
|
|
9
|
+
this.deltaTime= Number( config.deltaTime )*1000;
|
|
10
|
+
this.minData = Number( config.minData );
|
|
11
|
+
this.filter = Number( config.filter ?? 0 )*1000;
|
|
12
|
+
|
|
13
|
+
node.on('input', function(msg,send,done) {
|
|
14
|
+
if( msg.invalid )
|
|
15
|
+
{
|
|
16
|
+
done();
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
if( msg.reset || msg.topic==="init" )
|
|
20
|
+
{
|
|
21
|
+
context.set( "data", {} );
|
|
22
|
+
context.set( "last", {} );
|
|
23
|
+
node.status( "" );
|
|
24
|
+
}
|
|
25
|
+
else
|
|
26
|
+
{
|
|
27
|
+
const now = Date.now();
|
|
28
|
+
const payload = Number( RED.util.getMessageProperty( msg, node.property ) );
|
|
29
|
+
if( ! isNaN( payload ) )
|
|
30
|
+
{
|
|
31
|
+
let data = context.get( "data" ) ?? {};
|
|
32
|
+
let item = data[msg.topic] ?? [];
|
|
33
|
+
item.push( { time:now, value:payload } );
|
|
34
|
+
while( item[0].time < now - this.deltaTime )
|
|
35
|
+
{
|
|
36
|
+
item.shift();
|
|
37
|
+
}
|
|
38
|
+
data[msg.topic] = item;
|
|
39
|
+
context.set( "data", data );
|
|
40
|
+
|
|
41
|
+
if( item.length >= this.minData )
|
|
42
|
+
{
|
|
43
|
+
let sum = 0;
|
|
44
|
+
for( const value of item )
|
|
45
|
+
{
|
|
46
|
+
sum += value.value;
|
|
47
|
+
}
|
|
48
|
+
msg.payload = sum/item.length;
|
|
49
|
+
let last = context.get( "last" ) ?? {};
|
|
50
|
+
if( (last[msg.topic]??0)+this.filter < now )
|
|
51
|
+
{
|
|
52
|
+
last[msg.topic] = now;
|
|
53
|
+
context.set( "last", last );
|
|
54
|
+
node.status({fill:"green",shape:"dot",text:`${item.length} / ${msg.payload.toPrecision(4)}`});
|
|
55
|
+
send( msg );
|
|
56
|
+
}
|
|
57
|
+
else
|
|
58
|
+
{
|
|
59
|
+
node.status({fill:"gray",shape:"dot",text:`${item.length} / ${msg.payload.toPrecision(4)}`});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
else
|
|
63
|
+
{
|
|
64
|
+
node.status({fill:"gray",shape:"dot",text:"to less data"});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
else
|
|
68
|
+
{
|
|
69
|
+
node.status({fill:"red",shape:"dot",text:"payload is NaN"});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
done();
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
RED.nodes.registerType("mean",MeanNode);
|
|
77
|
+
}
|
package/math_statistics.html
CHANGED
|
@@ -10,8 +10,7 @@
|
|
|
10
10
|
},
|
|
11
11
|
minData:{value:"1", required:true, validate:function(v){
|
|
12
12
|
return v!=='' && !isNaN(v) && Number(v)>0;}
|
|
13
|
-
}
|
|
14
|
-
filter:{value:false}
|
|
13
|
+
}
|
|
15
14
|
},
|
|
16
15
|
inputs:1,
|
|
17
16
|
outputs:1,
|
|
@@ -52,10 +51,6 @@
|
|
|
52
51
|
<label for="node-input-minData"><i class="fa fa-check-square-o "></i> min. Data</label>
|
|
53
52
|
<input type="text" id="node-input-minData">
|
|
54
53
|
</div>
|
|
55
|
-
<div class="form-row">
|
|
56
|
-
<label for="node-input-filter"><i class="fa fa-filter"></i> Filter</label>
|
|
57
|
-
<input type="checkbox" id="node-input-filter" style="display:inline-block; width:20px; vertical-align:baseline;">
|
|
58
|
-
</div>
|
|
59
54
|
</script>
|
|
60
55
|
|
|
61
56
|
<script type="text/html" data-help-name="statistics">
|
|
@@ -104,7 +99,5 @@
|
|
|
104
99
|
<dd> time frame to collect the data for the analysis.</dd>
|
|
105
100
|
<dt>min. Data <span class="property-type">number</span></dt>
|
|
106
101
|
<dd> min. amount of data points to generate an output.</dd>
|
|
107
|
-
<dt>Filter <span class="property-type">boolean</span></dt>
|
|
108
|
-
<dd> block sending of unchanged <code>property</code>.</dd>
|
|
109
102
|
</dl>
|
|
110
103
|
</script>
|
package/math_statistics.js
CHANGED
|
@@ -8,7 +8,6 @@ module.exports = function(RED) {
|
|
|
8
8
|
this.property = config.property || "payload";
|
|
9
9
|
this.deltaTime= Number( config.deltaTime )*1000;
|
|
10
10
|
this.minData = Number( config.minData );
|
|
11
|
-
this.filter = Boolean( config.filter );
|
|
12
11
|
|
|
13
12
|
node.on('input', function(msg,send,done) {
|
|
14
13
|
if( msg.invalid )
|
|
@@ -19,7 +18,6 @@ module.exports = function(RED) {
|
|
|
19
18
|
if( msg.reset || msg.topic==="init" )
|
|
20
19
|
{
|
|
21
20
|
context.set( "data", {} );
|
|
22
|
-
context.set( "last", {} );
|
|
23
21
|
node.status( "" );
|
|
24
22
|
}
|
|
25
23
|
else
|
|
@@ -40,52 +38,33 @@ module.exports = function(RED) {
|
|
|
40
38
|
|
|
41
39
|
if( item.length >= this.minData )
|
|
42
40
|
{
|
|
43
|
-
|
|
41
|
+
msg.stat = {
|
|
42
|
+
count: item.length,
|
|
43
|
+
min: Number.MAX_SAFE_INTEGER,
|
|
44
|
+
max: Number.MIN_SAFE_INTEGER };
|
|
45
|
+
let sum = 0;
|
|
46
|
+
for( const value of item )
|
|
44
47
|
{
|
|
45
|
-
|
|
46
|
-
if(
|
|
48
|
+
sum += value.value;
|
|
49
|
+
if( value.value < msg.stat.min )
|
|
47
50
|
{
|
|
48
|
-
|
|
49
|
-
context.set( "last", last );
|
|
50
|
-
return true;
|
|
51
|
+
msg.stat.min = value.value;
|
|
51
52
|
}
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if( !node.filter || checkFilter() )
|
|
56
|
-
{
|
|
57
|
-
msg.stat = {
|
|
58
|
-
count: item.length,
|
|
59
|
-
min: Number.MAX_SAFE_INTEGER,
|
|
60
|
-
max: Number.MIN_SAFE_INTEGER };
|
|
61
|
-
let sum = 0;
|
|
62
|
-
for( const value of item )
|
|
63
|
-
{
|
|
64
|
-
sum += value.value;
|
|
65
|
-
if( value.value < msg.stat.min )
|
|
66
|
-
{
|
|
67
|
-
msg.stat.min = value.value;
|
|
68
|
-
}
|
|
69
|
-
if( value.value > msg.stat.max )
|
|
70
|
-
{
|
|
71
|
-
msg.stat.max = value.value;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
msg.stat.average = sum/msg.stat.count;
|
|
75
|
-
let varianz = 0;
|
|
76
|
-
for( const value of item )
|
|
53
|
+
if( value.value > msg.stat.max )
|
|
77
54
|
{
|
|
78
|
-
|
|
55
|
+
msg.stat.max = value.value;
|
|
79
56
|
}
|
|
80
|
-
msg.stat.deviation = Math.sqrt( varianz / msg.stat.count );
|
|
81
|
-
msg.stat.variation = msg.stat.deviation / msg.stat.average;
|
|
82
|
-
node.status({fill:"green",shape:"dot",text:`${msg.stat.count} / ${msg.stat.deviation.toPrecision(4)}`});
|
|
83
|
-
send( msg );
|
|
84
57
|
}
|
|
85
|
-
|
|
58
|
+
msg.stat.average = sum/msg.stat.count;
|
|
59
|
+
let varianz = 0;
|
|
60
|
+
for( const value of item )
|
|
86
61
|
{
|
|
87
|
-
|
|
62
|
+
varianz += ( value.value - msg.stat.average ) ** 2;
|
|
88
63
|
}
|
|
64
|
+
msg.stat.deviation = Math.sqrt( varianz / msg.stat.count );
|
|
65
|
+
msg.stat.variation = msg.stat.deviation / msg.stat.average;
|
|
66
|
+
node.status({fill:"green",shape:"dot",text:`${msg.stat.count} / ${msg.stat.deviation.toPrecision(4)}`});
|
|
67
|
+
send( msg );
|
|
89
68
|
}
|
|
90
69
|
else
|
|
91
70
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mschaeffler/node-red-logic",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "nodes for simple operations.",
|
|
5
5
|
"license": "LGPL-2.1",
|
|
6
6
|
"homepage": "https://github.com/m-schaeffler/node-red-my-nodes/tree/main/node-red-logic",
|
|
@@ -28,7 +28,8 @@
|
|
|
28
28
|
"fallingEdge":"math_falling.js",
|
|
29
29
|
"raisingEdge":"math_raising.js",
|
|
30
30
|
"hysteresisEdge":"math_hysteresis.js",
|
|
31
|
-
"statistics":"math_statistics.js"
|
|
31
|
+
"statistics":"math_statistics.js",
|
|
32
|
+
"mean":"math_mean.js"
|
|
32
33
|
}
|
|
33
34
|
},
|
|
34
35
|
"author": {
|