@mschaeffler/node-red-logic 1.1.0 → 1.2.0

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 ADDED
@@ -0,0 +1,90 @@
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
+ },
15
+ inputs:1,
16
+ outputs:1,
17
+ icon:"font-awesome/fa-arrows-v",
18
+ label: function() {
19
+ return this.name||"mean";
20
+ },
21
+ labelStyle: function() {
22
+ return this.name?"node_label_italic":"";
23
+ },
24
+ inputLabels: 'input',
25
+ outputLabels: 'output',
26
+ oneditprepare: function() {
27
+ $("#node-input-property").typedInput({
28
+ type:"msg",
29
+ types:["msg"],
30
+ typeField: "#node-input-property-type"
31
+ });
32
+ }
33
+ });
34
+ </script>
35
+
36
+ <script type="text/html" data-template-name="mean">
37
+ <div class="form-row">
38
+ <label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
39
+ <input type="text" id="node-input-name" placeholder="Name">
40
+ </div>
41
+ <div class="form-row">
42
+ <label for="node-input-input"><i class="fa fa-ellipsis-h"></i> Eigenschaft</label>
43
+ <input type="text" id="node-input-property">
44
+ <input type="hidden" id="node-input-property-type">
45
+ </div>
46
+ <div class="form-row">
47
+ <label for="node-input-minMean"><i class="fa fa-hourglass-o"></i> Zeitspanne</label>
48
+ <input type="text" id="node-input-deltaTime">
49
+ </div>
50
+ <div class="form-row">
51
+ <label for="node-input-minData"><i class="fa fa-check-square-o "></i> min. Data</label>
52
+ <input type="text" id="node-input-minData">
53
+ </div>
54
+ </script>
55
+
56
+ <script type="text/html" data-help-name="mean">
57
+
58
+ <p>Calculate mean value of one or more data streams.</p>
59
+ <p>If the msg property <code>invalid</code> is present in the message, all nodes ignore the message.</p>
60
+
61
+ <h3>Input</h3>
62
+
63
+ <p>The different inputs are differentiated by topics.</p>
64
+ <p>The message property to be used as payload can be defined with the <code>Property</code> property.</p>
65
+
66
+ <dl class="message-properties">
67
+ <dt>topic <span class="property-type">string</span></dt>
68
+ <dd> name of the input channel.</dd>
69
+ <dt>payload <span class="property-type">number</span></dt>
70
+ <dd> input value for <code>topic</code>.</dd>
71
+ </dl>
72
+
73
+ <h3>Output</h3>
74
+
75
+ <dl class="message-properties">
76
+ <dt>payload <span class="property-type">number</span></dt>
77
+ <dd> average value.</dd>
78
+ </dl>
79
+
80
+ <h3>Parameter</h3>
81
+
82
+ <dl class="message-properties">
83
+ <dt>Property <span class="property-type">string</span></dt>
84
+ <dd> defines the message property to be used as payload.</dd>
85
+ <dt>Delta time <span class="property-type">number</span></dt>
86
+ <dd> time frame to collect the data for the analysis.</dd>
87
+ <dt>min. Data <span class="property-type">number</span></dt>
88
+ <dd> min. amount of data points to generate an output.</dd>
89
+ </dl>
90
+ </script>
package/math_mean.js ADDED
@@ -0,0 +1,66 @@
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
+
12
+ node.on('input', function(msg,send,done) {
13
+ if( msg.invalid )
14
+ {
15
+ done();
16
+ return null;
17
+ }
18
+ if( msg.reset || msg.topic==="init" )
19
+ {
20
+ context.set( "data", {} );
21
+ context.set( "last", {} );
22
+ node.status( "" );
23
+ }
24
+ else
25
+ {
26
+ const now = Date.now();
27
+ const payload = Number( RED.util.getMessageProperty( msg, node.property ) );
28
+ if( ! isNaN( payload ) )
29
+ {
30
+ let data = context.get( "data" ) ?? {};
31
+ let item = data[msg.topic] ?? [];
32
+ item.push( { time:now, value:payload } );
33
+ while( item[0].time < now - this.deltaTime )
34
+ {
35
+ item.shift();
36
+ }
37
+ data[msg.topic] = item;
38
+ context.set( "data", data );
39
+
40
+ if( item.length >= this.minData )
41
+ {
42
+ let sum = 0;
43
+ for( const value of item )
44
+ {
45
+ sum += value.value;
46
+ }
47
+ msg.payload = sum/item.length;
48
+ node.status({fill:"green",shape:"dot",text:`${item.length} / ${msg.payload.toPrecision(4)}`});
49
+ send( msg );
50
+ }
51
+ else
52
+ {
53
+ node.status({fill:"gray",shape:"dot",text:"to less data"});
54
+ }
55
+ }
56
+ else
57
+ {
58
+ node.status({fill:"red",shape:"dot",text:"payload is NaN"});
59
+ }
60
+ }
61
+ done();
62
+ });
63
+ }
64
+
65
+ RED.nodes.registerType("mean",MeanNode);
66
+ }
@@ -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>
@@ -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 )
@@ -40,52 +39,33 @@ module.exports = function(RED) {
40
39
 
41
40
  if( item.length >= this.minData )
42
41
  {
43
- function checkFilter()
42
+ msg.stat = {
43
+ count: item.length,
44
+ min: Number.MAX_SAFE_INTEGER,
45
+ max: Number.MIN_SAFE_INTEGER };
46
+ let sum = 0;
47
+ for( const value of item )
44
48
  {
45
- let last = context.get( "last" ) ?? {};
46
- if( payload !== last[msg.topic] )
49
+ sum += value.value;
50
+ if( value.value < msg.stat.min )
47
51
  {
48
- last[msg.topic] = payload;
49
- context.set( "last", last );
50
- return true;
52
+ msg.stat.min = value.value;
51
53
  }
52
- return false;
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 )
54
+ if( value.value > msg.stat.max )
77
55
  {
78
- varianz += ( value.value - msg.stat.average ) ** 2;
56
+ msg.stat.max = value.value;
79
57
  }
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
58
  }
85
- else
59
+ msg.stat.average = sum/msg.stat.count;
60
+ let varianz = 0;
61
+ for( const value of item )
86
62
  {
87
- node.status({fill:"gray",shape:"dot",text:item.length});
63
+ varianz += ( value.value - msg.stat.average ) ** 2;
88
64
  }
65
+ msg.stat.deviation = Math.sqrt( varianz / msg.stat.count );
66
+ msg.stat.variation = msg.stat.deviation / msg.stat.average;
67
+ node.status({fill:"green",shape:"dot",text:`${msg.stat.count} / ${msg.stat.deviation.toPrecision(4)}`});
68
+ send( msg );
89
69
  }
90
70
  else
91
71
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mschaeffler/node-red-logic",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
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": {