# node-red-contrib-boolean-logic-ultimate | |||||
<p> | |||||
<b>Version 0.0.1</b><br/> | |||||
- Initial release<br/> | |||||
</p> |
# node-red-contrib-boolean-logic | |||||
[Node-RED](http://nodered.org/) nodes to easily perform boolean logic. | |||||
## The problem | |||||
[Node-RED](http://nodered.org/) does not support multiple inputs on nodes, and it has been discussed at length in [this thread](https://groups.google.com/forum/#!searchin/node-red/multiple$20inputs%7Csort:relevance/node-red/Q0YLQYCUJ_E/JVNjznmx2e8J). The TL;DR - as I understand it - is that the developers of NR argue that multiple inputs makes it too complex for users without a background in electrical engineering and that it is [preferred](https://groups.google.com/d/msg/node-red/Q0YLQYCUJ_E/DTxHFcVfAwAJ) users of NR instead use other means to create the desired logic (i.e. write Javascript in function-nodes). | |||||
## A solution | |||||
I really needed a simple and reusable way to perform boolean logic on multiple topics without the need to write the same code over and over. | |||||
Could this be solved using a subflow? No, function-node within a subflow cannot be configured on an instance basis which is required as the logic must know how many inputs it is expecting when performing operations such as ```A || B``` or ```A && (B || C)```. Yes, that could be hard coded, but then it would not be reusable. Also, a subflow cannot use the status indicator which is a great help to the user. | |||||
What I came up with are the following nodes. | |||||
* BooleanLogic: Can perform AND, OR and XOR operations on ```msg.payload``` on any number of topics. | |||||
* Invert: Inverts the ```msg.payload```, e.g. true -> false. | |||||
* Debug: A debug node that displays the status direcly in the editor, making it easier to see the boolean value at a specific point. | |||||
All nodes attempts to convert the incoming ```msg.payload``` to a boolean value according to these rules: | |||||
* Boolean values are taken as-is. | |||||
* For numbers, 0 evaluates to false, all other numbers evaluates to true. | |||||
* Strings are converted to numbers if they match the format of a decimal value, then the same rule as for numbers are applied. If it does not match, it evaluates to false. Also, the string "true" evaluates to true. | |||||
### BooleanLogic | |||||
This node must be configured with the expected number of topics. It will not output a value until it has received the configured number of topics. Also, if it receives more than the configured number of topics it will reset (but not output a value) and wait until it once again sees the configured number of topics. | |||||
## Example | |||||
![Example](http://i.imgur.com/m2s6JRl.png) | |||||
## Version history | |||||
* 0.0.1 First release | |||||
* 0.0.2 | |||||
* Changed status indicators from dot to rings for false-values. | |||||
* Reworked the conversion of input values to be consistent between numbers and strings with numeric meaning. | |||||
# node-red-contrib-boolean-logic-ultimate | |||||
## DESCRIPTION | |||||
The node performs Boolean logic on the incoming payloads.<br/> | |||||
The node performs 3 checks (<b>AND,OR,XOR</b>) on the incoming boolean payloads and outputs the result at the same time, as follow:<br/> | |||||
- Output "AND": true or false<br/> | |||||
- Output "OR": true or false<br/> | |||||
- Output "XOR": true or false<br/> | |||||
The node can have a persistent input: the input values are retained after a node-red reboot. That means, that if you reboot your node-red, you don't need to wait all inputs to arrive and initialize the node, before the node can output a payload. | |||||
## CHANGELOG | |||||
* See <a href="https://github.com/Supergiovane/node-red-contrib-boolean-logic-ultimate/blob/master/CHANGELOG.md">here the changelog</a> | |||||
## CONFIGURATION | |||||
<p> | |||||
The node expects a fixed number of topics (configured in the settings) on which it will operate. It will only output a value | |||||
when it has seen the expected number of topics. If it ever sees more than the configured number of topics it will log a message then reset its state and start over.<br/> | |||||
Changing the topic is usually only needed when chaining multiple boolean nodes after each other becuse the topics will then all be the same when delivered to the nodes further down the chain.<br/> | |||||
<br/> | |||||
<b>Filter output result</b><br /> | |||||
<ol> | |||||
<li>Output both 'true' and 'false' results: Standard behaviour, the node will output <b>true</b> and <b>false</b> whenever it receives an input and calculate the boolean logics as output.</li> | |||||
<li>Output only 'true' results: whenever the node receives an input, it outputs a payload <b>true</b> only if the result of the logic is true. <b>False</b> results are filtered out.</li> | |||||
</ol> | |||||
<br/> | |||||
<b>Remember latest input values after reboot</b><br /> | |||||
If checked, the input values are retained after a node-red reboot. That means, that if you reboot your node-red, you don't need to wait all inputs to arrive and initialize the node, before the node can output a payload.<br/> | |||||
Every time you modify the node's config, <b>the retained values are cleared</b>.<br/> | |||||
<br/> | |||||
All incoming msg.payloads are converted into a boolean value according to the following rules (this applies to all boolean logic nodes): | |||||
<ol> | |||||
<li>Boolean values are taken as-is.</li> | |||||
<li>For numbers, 0 evaluates to false, all other numbers evaluates to true.</li> | |||||
<li>Strings are converted to numbers if they match the format of a decimal value, then the same rule as for numbers are applied. If it does not match, it evaluates to false. Also, the string "true" evaluates to true.</li> | |||||
</ol> | |||||
<br> | |||||
The XOR operation operates in a one, and only one mode, i.e. (A ^ B) ^ C ... ^ n | |||||
</p> |
<!-- | |||||
Licensed under the MIT license, see LICENSE file. | |||||
Author: Per Malmberg (https://github.com/PerMalmberg) | |||||
--> | |||||
<script type="text/javascript"> | <script type="text/javascript"> | ||||
RED.nodes.registerType('BDebug',{ | RED.nodes.registerType('BDebug',{ | ||||
category: 'boolean logic', | |||||
color: '#C0DEED', | |||||
category: 'boolean logic ultimate', | |||||
color: '#ff8080', | |||||
defaults: { | defaults: { | ||||
name: { | name: { | ||||
value: "Debug" | value: "Debug" |
// Licensed under the MIT license, see LICENSE file. | |||||
// Author: Per Malmberg (https://github.com/PerMalmberg) | |||||
module.exports = function(RED) { | module.exports = function(RED) { | ||||
function BDebug(config) { | function BDebug(config) { | ||||
RED.nodes.createNode(this,config); | RED.nodes.createNode(this,config); |
<script type="text/javascript"> | |||||
RED.nodes.registerType('BooleanLogicUltimate',{ | |||||
category: 'boolean logic ultimate', | |||||
color: '#ff8080', | |||||
defaults: { | |||||
name: { | |||||
value: "" | |||||
}, | |||||
filtertrue: {value:"both"}, | |||||
persist: {value:true}, | |||||
inputCount: { | |||||
value: 2, | |||||
required: true, | |||||
validate: | |||||
function(v) { | |||||
return !isNaN( parseInt( v ) ) && parseInt( v ) >= 2; | |||||
} | |||||
}, | |||||
topic: { | |||||
value: "result", | |||||
required: true, | |||||
validate: | |||||
function(v) { | |||||
return v !== undefined && v.length > 0; | |||||
} | |||||
} | |||||
}, | |||||
inputs:1, | |||||
outputs:3, | |||||
outputLabels: function(i) { | |||||
var ret=""; | |||||
switch (i) { | |||||
case 0: | |||||
return "AND"; | |||||
break; | |||||
case 1: | |||||
return "OR"; | |||||
break; | |||||
case 2 | |||||
: | |||||
return "XOR"; | |||||
break; | |||||
default: | |||||
break; | |||||
} | |||||
}, | |||||
icon: "serial.png", | |||||
label: | |||||
function() { | |||||
var label = "Logic" + " [" + this.inputCount + "]"; | |||||
if( this.name !== undefined && this.name.length > 0 ) { | |||||
label = label + " (" + this.name + ")"; | |||||
} | |||||
return label; | |||||
}, | |||||
paletteLabel: function() { | |||||
return "Boolean Logic Ultimate"; | |||||
}, | |||||
oneditsave: function () { | |||||
} | |||||
}); | |||||
</script> | |||||
<script type="text/x-red" data-template-name="BooleanLogicUltimate"> | |||||
<div class="form-row"> | |||||
<label for="node-input-filtertrue"><i class="icon-tag"></i> Filter output result</label> | |||||
<select type="text" id="node-input-filtertrue" placeholder="Filter"> | |||||
<option value="both">Output both 'true' and 'false' results</option> | |||||
<option value="onlytrue">Output only 'true' results</option> | |||||
</select> | |||||
</div> | |||||
<div class="form-row"> | |||||
<input type="checkbox" id="node-input-persist" style="display:inline-block; width:auto; vertical-align:top;"> | |||||
<label style="width:auto" for="node-input-persist"> Remember latest input values after reboot</label> | |||||
<div id="helpallga"><i> If checked, the input values are retained after a node-red reboot. That means, that if you reboot your node-red, you don't need to wait all inputs to arrive and initialize the node, before the node can output a payload.</div> | |||||
</div> | |||||
<div class="form-row"> | |||||
<label for="node-input-inputCount"><i class="icon-tag"></i> Number of topics</label> | |||||
<input type="text" id="node-input-inputCount" placeholder="Number of topics to consider"> | |||||
</div> | |||||
<div class="form-row"> | |||||
<label for="node-input-name"><i class="icon-tag"></i> Name</label> | |||||
<input type="text" id="node-input-name" placeholder="Name"> | |||||
</div> | |||||
<div class="form-row"> | |||||
<label for="node-input-topic"><i class="icon-tag"></i> Topic</label> | |||||
<input type="text" id="node-input-topic" placeholder="Topic"> | |||||
</div> | |||||
</script> | |||||
<script type="text/x-red" data-help-name="BooleanLogicUltimate"> | |||||
<p>The node performs Boolean logic on the incoming payloads.<br/> | |||||
The node performs 3 checks (<b>AND,OR,XOR</b>) on the incoming boolean payloads and outputs the result at the same time, as follow:<br/> | |||||
- Output "AND": true or false<br/> | |||||
- Output "OR": true or false<br/> | |||||
- Output "XOR": true or false<br/> | |||||
<br/> | |||||
Changing the topic is usually only needed when chaining multiple boolean nodes after each other becuse the topics will then all be the same when delivered to the nodes further down the chain.<br/> | |||||
<br/> | |||||
The node expects a fixed number of topics (configured in the settings) on which it will operate. It will only output a value | |||||
when it has seen the expected number of topics. If it ever sees more than the configured number of topics it will log a message then reset its state and start over.<br/> | |||||
<br/><br/> | |||||
<b>Filter output result</b><br /> | |||||
<ol> | |||||
<li>Output both 'true' and 'false' results: Standard behaviour, the node will output <b>true</b> and <b>false</b> whenever it receives an input and calculate the boolean logics as output.</li> | |||||
<li>Output only 'true' results: whenever the node receives an input, it outputs a payload <b>true</b> only if the result of the logic is true. <b>False</b> results are filtered out.</li> | |||||
</ol> | |||||
<br/><br/> | |||||
<b>Remember latest input values after reboot</b><br /> | |||||
If checked, the input values are retained after a node-red reboot. That means, that if you reboot your node-red, you don't need to wait all inputs to arrive and initialize the node, before the node can output a payload.<br/> | |||||
Every time you modify the node's config, <b>the retained values are cleared</b>.<br/> | |||||
<br/> | |||||
All incoming msg.payloads are converted into a boolean value according to the following rules (this applies to all boolean logic nodes): | |||||
<ol> | |||||
<li>Boolean values are taken as-is.</li> | |||||
<li>For numbers, 0 evaluates to false, all other numbers evaluates to true.</li> | |||||
<li>Strings are converted to numbers if they match the format of a decimal value, then the same rule as for numbers are applied. If it does not match, it evaluates to false. Also, the string "true" evaluates to true.</li> | |||||
</ol> | |||||
<br> | |||||
The XOR operation operates in a one, and only one mode, i.e. (A ^ B) ^ C ... ^ n | |||||
</p> | |||||
</script> |
module.exports = function(RED) { | |||||
function BooleanLogicUltimate(config) { | |||||
RED.nodes.createNode(this,config); | |||||
this.config = config; | |||||
this.state = {}; | |||||
var node = this; | |||||
var NodeHelper = require('./NodeHelper.js'); | |||||
var fs = require('fs'); | |||||
var h = new NodeHelper(node); | |||||
// Delete persistent states on change/deploy | |||||
DeletePersistFile(); | |||||
// Populate the state array with the persisten file | |||||
if (this.config.persist == true) { | |||||
try { | |||||
var contents = fs.readFileSync(node.id.toString()).toString(); | |||||
if (typeof contents !== 'undefined') { | |||||
node.state = JSON.parse(contents); | |||||
node.status({fill: "blue",shape: "ring",text: "Loaded persistent states (" + Object.keys(this.state).length + " total)."}); | |||||
} | |||||
} catch (error) { | |||||
node.status({fill: "grey",shape: "ring",text: "No persistent states"}); | |||||
} | |||||
} else { | |||||
node.status({fill: "yellow",shape: "dot",text: "Waiting for input states"}); | |||||
} | |||||
this.on('input', function (msg) { | |||||
var topic = msg.topic; | |||||
var payload = msg.payload; | |||||
if (topic !== undefined && payload !== undefined) { | |||||
var value = h.ToBoolean( payload ); | |||||
var state = node.state; | |||||
state[topic] = value; | |||||
// Sabe the state array to a perisistent file | |||||
if (this.config.persist == true) { | |||||
fs.writeFileSync(this.id.toString(),JSON.stringify(state)); | |||||
} | |||||
// Do we have as many inputs as we expect? | |||||
var keyCount = Object.keys(state).length; | |||||
if( keyCount == node.config.inputCount ) { | |||||
var resAND = CalculateResult("AND"); | |||||
var resOR = CalculateResult("OR"); | |||||
var resXOR = CalculateResult("XOR"); | |||||
if (this.config.filtertrue == "onlytrue") { | |||||
if (!resAND) { resAND = null }; | |||||
if (!resOR) { resOR = null }; | |||||
if (!resXOR) { resXOR = null }; | |||||
} | |||||
h.SetResult(resAND,resOR,resXOR, node.config.topic); | |||||
} | |||||
else if(keyCount > node.config.inputCount ) { | |||||
node.warn( | |||||
(node.config.name !== undefined && node.config.name.length > 0 | |||||
? node.config.name : "BooleanLogicUltimate") | |||||
+ " [Logic]: More than the specified " | |||||
+ node.config.inputCount + " topics received, resetting. Will not output new value until " + node.config.inputCount + " new topics have been received."); | |||||
node.state = {}; | |||||
h.DisplayUnkownStatus(); | |||||
} | |||||
} | |||||
}); | |||||
function DeletePersistFile (){ | |||||
// Detele the persist file | |||||
try { | |||||
fs.unlinkSync(node.id.toString()); | |||||
node.status({fill: "red",shape: "ring",text: "Persistent states deleted."}); | |||||
} catch (error) { | |||||
node.status({fill: "red",shape: "ring",text: "Error deleting persistent file: " + error.toString()}); | |||||
} | |||||
} | |||||
function CalculateResult(_operation) { | |||||
var res; | |||||
if( _operation == "XOR") { | |||||
res = PerformXOR(); | |||||
} | |||||
else { | |||||
// We need a starting value to perform AND and OR operations. | |||||
var keys = Object.keys(node.state); | |||||
res = node.state[keys[0]]; | |||||
for( var i = 1; i < keys.length; ++i ) { | |||||
var key = keys[i]; | |||||
res = PerformSimpleOperation( _operation, res, node.state[key] ); | |||||
} | |||||
} | |||||
return res; | |||||
} | |||||
function PerformXOR() | |||||
{ | |||||
// XOR = exclusively one input is true. As such, we just count the number of true values and compare to 1. | |||||
var trueCount = 0; | |||||
for( var key in node.state ) { | |||||
if( node.state[key] ) { | |||||
trueCount++; | |||||
} | |||||
} | |||||
return trueCount == 1; | |||||
} | |||||
function PerformSimpleOperation( operation, val1, val2 ) { | |||||
var res; | |||||
if( operation === "AND" ) { | |||||
res = val1 && val2; | |||||
} | |||||
else if( operation === "OR" ) { | |||||
res = val1 || val2; | |||||
} | |||||
else { | |||||
node.error( "Unknown operation: " + operation ); | |||||
} | |||||
return res; | |||||
} | |||||
} | |||||
RED.nodes.registerType("BooleanLogicUltimate",BooleanLogicUltimate); | |||||
} |
<!-- | |||||
Licensed under the MIT license, see LICENSE file. | |||||
Author: Per Malmberg (https://github.com/PerMalmberg) | |||||
--> | |||||
<script type="text/javascript"> | <script type="text/javascript"> | ||||
RED.nodes.registerType('Invert',{ | RED.nodes.registerType('Invert',{ | ||||
category: 'boolean logic', | |||||
color: '#C0DEED', | |||||
category: 'boolean logic ultimate', | |||||
color: '#ff8080', | |||||
defaults: { | defaults: { | ||||
name: { | name: { | ||||
value: "Invert" | value: "Invert" |
// Licensed under the MIT license, see LICENSE file. | |||||
// Author: Per Malmberg (https://github.com/PerMalmberg) | |||||
module.exports = function(RED) { | module.exports = function(RED) { | ||||
function Invert(config) { | function Invert(config) { | ||||
RED.nodes.createNode(this,config); | RED.nodes.createNode(this,config); |
// Licensed under the MIT license, see LICENSE file. | |||||
// Author: Per Malmberg (https://github.com/PerMalmberg) | |||||
var NodeHelper = function( node ) { | var NodeHelper = function( node ) { | ||||
var myNode = node; | var myNode = node; | ||||
var self = this; | var self = this; | ||||
}); | }); | ||||
}; | }; | ||||
this.SetResult = function( value, optionalTopic ) { | |||||
self.DisplayStatus( value ); | |||||
this.SetResult = function( _valueAND, _valueOR, _valueXOR, optionalTopic ) { | |||||
self.DisplayStatus( "AND:" + _valueAND + " OR:" +_valueOR + " XOR:" +_valueXOR); | |||||
var msg = { | |||||
topic: optionalTopic === undefined ? "result" : optionalTopic, | |||||
payload: value | |||||
}; | |||||
myNode.send(msg); | |||||
if (_valueAND!=null){ | |||||
var msgAND = { | |||||
topic: optionalTopic === undefined ? "result" : optionalTopic, | |||||
operation:"AND", | |||||
payload: _valueAND | |||||
}; | |||||
} | |||||
if (_valueOR!=null){ | |||||
var msgOR = { | |||||
topic: optionalTopic === undefined ? "result" : optionalTopic, | |||||
operation:"OR", | |||||
payload: _valueOR | |||||
}; | |||||
} | |||||
if (_valueXOR!=null){ | |||||
var msgXOR = { | |||||
topic: optionalTopic === undefined ? "result" : optionalTopic, | |||||
operation:"XOR", | |||||
payload: _valueXOR | |||||
}; | |||||
} | |||||
myNode.send([msgAND,msgOR,msgXOR]); | |||||
}; | }; | ||||
}; | }; | ||||
module.exports = NodeHelper; | module.exports = NodeHelper; |
<!-- | |||||
Licensed under the MIT license, see LICENSE file. | |||||
Author: Per Malmberg (https://github.com/PerMalmberg) | |||||
--> | |||||
<script type="text/javascript"> | |||||
RED.nodes.registerType('BooleanLogic',{ | |||||
category: 'boolean logic', | |||||
color: '#C0DEED', | |||||
defaults: { | |||||
name: { | |||||
value: "" | |||||
}, | |||||
operation: { | |||||
value: "AND", | |||||
required: true, | |||||
validate: | |||||
function(v) { | |||||
return v !== undefined && v.length > 0; | |||||
} | |||||
}, | |||||
inputCount: { | |||||
value: 2, | |||||
required: true, | |||||
validate: | |||||
function(v) { | |||||
return !isNaN( parseInt( v ) ) && parseInt( v ) >= 2; | |||||
} | |||||
}, | |||||
topic: { | |||||
value: "result", | |||||
required: true, | |||||
validate: | |||||
function(v) { | |||||
return v !== undefined && v.length > 0; | |||||
} | |||||
} | |||||
}, | |||||
inputs:1, | |||||
outputs:1, | |||||
icon: "serial.png", | |||||
label: | |||||
function() { | |||||
var label = this.operation + " [" + this.inputCount + "]"; | |||||
if( this.name !== undefined && this.name.length > 0 ) { | |||||
label = label + " (" + this.name + ")"; | |||||
} | |||||
return label; | |||||
}, | |||||
paletteLabel: function() { | |||||
return "Boolean logic"; | |||||
} | |||||
}); | |||||
</script> | |||||
<script type="text/x-red" data-template-name="BooleanLogic"> | |||||
<div class="form-row"> | |||||
<label for="node-input-operation"><i class="icon-tag"></i> Operation</label> | |||||
<select type="text" id="node-input-operation" placeholder="Operation"> | |||||
<option value="AND">And</option> | |||||
<option value="OR">Or</option> | |||||
<option value="XOR">Exclusive Or</option> | |||||
</select> | |||||
</div> | |||||
<div class="form-row"> | |||||
<label for="node-input-inputCount"><i class="icon-tag"></i> Number of topics</label> | |||||
<input type="text" id="node-input-inputCount" placeholder="Number of topics to consider"> | |||||
</div> | |||||
<div class="form-row"> | |||||
<label for="node-input-name"><i class="icon-tag"></i> Name</label> | |||||
<input type="text" id="node-input-name" placeholder="Name"> | |||||
</div> | |||||
<div class="form-row"> | |||||
<label for="node-input-topic"><i class="icon-tag"></i> Topic</label> | |||||
<input type="text" id="node-input-topic" placeholder="Topic"> | |||||
</div> | |||||
</script> | |||||
<script type="text/x-red" data-help-name="BooleanLogic"> | |||||
<p>A node that performs Boolean logic on the incoming payloads.<br/> | |||||
Select the operation in the settings.<br/> | |||||
Changing the topic is usually only needed when chaining multiple boolean nodes after each other becuse the topics will then all be the same when delivered to the nodes further down the chain.<br/> | |||||
<br/> | |||||
The node expects a fixed number of topics (configured in the settings) on which it will operate. It will only output a value | |||||
when it has seen the expected number of topics. If it ever sees more than the configured number of topics it will log a message then reset its state and start over.<br/> | |||||
<br/> | |||||
Example:<br/> | |||||
The node has been configured for two topics, with the operation OR. | |||||
<br/> | |||||
<ol> | |||||
<li>Topic 'A' with value false arrives - only one topic seen so no output.</li> | |||||
<li>Topic 'B' with value true arrives - two topics seen so the node evaluates the received topics and outputs true.</li> | |||||
<li>Topic 'C' arrives. This is the third topic which is one more than the configured number so the node resets and will not output another value until | |||||
two new topics have been received. Note that when resetting it does not output any value.</li> | |||||
</ol> | |||||
<br/> | |||||
All incoming msg.payloads are converted into a boolean value according to the following rules (this applies to all boolean logic nodes): | |||||
<ol> | |||||
<li>Boolean values are taken as-is.</li> | |||||
<li>For numbers, 0 evaluates to false, all other numbers evaluates to true.</li> | |||||
<li>Strings are converted to numbers if they match the format of a decimal value, then the same rule as for numbers are applied. If it does not match, it evaluates to false. Also, the string "true" evaluates to true.</li> | |||||
</ol> | |||||
<br> | |||||
The XOR operation operates in a one, and only one mode, i.e. (A ^ B) ^ C ... ^ n | |||||
</p> | |||||
</script> |
// Licensed under the MIT license, see LICENSE file. | |||||
// Author: Per Malmberg (https://github.com/PerMalmberg) | |||||
module.exports = function(RED) { | |||||
function BooleanLogic(config) { | |||||
RED.nodes.createNode(this,config); | |||||
this.config = config; | |||||
this.state = {}; | |||||
var node = this; | |||||
var NodeHelper = require('./NodeHelper.js'); | |||||
var h = new NodeHelper( node ); | |||||
this.on('input', function(msg) { | |||||
var topic = msg.topic; | |||||
var payload = msg.payload; | |||||
if( topic !== undefined && payload !== undefined ) { | |||||
var value = h.ToBoolean( payload ); | |||||
var state = node.state; | |||||
state[topic] = value; | |||||
// Do we have as many inputs as we expect? | |||||
var keyCount = Object.keys(state).length; | |||||
if( keyCount == node.config.inputCount ) { | |||||
var res = CalculateResult(); | |||||
h.SetResult( res, node.config.topic ); | |||||
} | |||||
else if(keyCount > node.config.inputCount ) { | |||||
node.warn( | |||||
(node.config.name !== undefined && node.config.name.length > 0 | |||||
? node.config.name : "BooleanLogic") | |||||
+ " [" + node.config.operation + "]: More than the specified " | |||||
+ node.config.inputCount + " topics received, resetting. Will not output new value until " + node.config.inputCount + " new topics have been received."); | |||||
node.state = {}; | |||||
h.DisplayUnkownStatus(); | |||||
} | |||||
} | |||||
}); | |||||
function CalculateResult() { | |||||
var res; | |||||
if( node.config.operation == "XOR") { | |||||
res = PerformXOR(); | |||||
} | |||||
else { | |||||
// We need a starting value to perform AND and OR operations. | |||||
var keys = Object.keys(node.state); | |||||
res = node.state[keys[0]]; | |||||
for( var i = 1; i < keys.length; ++i ) { | |||||
var key = keys[i]; | |||||
res = PerformSimpleOperation( node.config.operation, res, node.state[key] ); | |||||
} | |||||
} | |||||
return res; | |||||
} | |||||
function PerformXOR() | |||||
{ | |||||
// XOR = exclusively one input is true. As such, we just count the number of true values and compare to 1. | |||||
var trueCount = 0; | |||||
for( var key in node.state ) { | |||||
if( node.state[key] ) { | |||||
trueCount++; | |||||
} | |||||
} | |||||
return trueCount == 1; | |||||
} | |||||
function PerformSimpleOperation( operation, val1, val2 ) { | |||||
var res; | |||||
if( operation === "AND" ) { | |||||
res = val1 && val2; | |||||
} | |||||
else if( operation === "OR" ) { | |||||
res = val1 || val2; | |||||
} | |||||
else { | |||||
node.error( "Unknown operation: " + operation ); | |||||
} | |||||
return res; | |||||
} | |||||
h.DisplayUnkownStatus(); | |||||
} | |||||
RED.nodes.registerType("BooleanLogic",BooleanLogic); | |||||
} |
# Security holding package | |||||
This package name is not currently in use, but was formerly occupied | |||||
by another package. To avoid malicious use, npm is hanging on to the | |||||
package name, but loosely, and we'll probably give it to you if you | |||||
want it. | |||||
You may adopt this package by contacting support@npmjs.com and | |||||
requesting the name. |
{ | |||||
"_from": "fs@0.0.1-security", | |||||
"_id": "fs@0.0.1-security", | |||||
"_inBundle": false, | |||||
"_integrity": "sha1-invTcYa23d84E/I4WLV+yq9eQdQ=", | |||||
"_location": "/fs", | |||||
"_phantomChildren": {}, | |||||
"_requested": { | |||||
"type": "version", | |||||
"registry": true, | |||||
"raw": "fs@0.0.1-security", | |||||
"name": "fs", | |||||
"escapedName": "fs", | |||||
"rawSpec": "0.0.1-security", | |||||
"saveSpec": null, | |||||
"fetchSpec": "0.0.1-security" | |||||
}, | |||||
"_requiredBy": [ | |||||
"/" | |||||
], | |||||
"_resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", | |||||
"_shasum": "8a7bd37186b6dddf3813f23858b57ecaaf5e41d4", | |||||
"_spec": "fs@0.0.1-security", | |||||
"_where": "/Users/massimosaccani/Documents/GitHub/node-red-contrib-boolean-logic-ultimate", | |||||
"author": "", | |||||
"bugs": { | |||||
"url": "https://github.com/npm/security-holder/issues" | |||||
}, | |||||
"bundleDependencies": false, | |||||
"deprecated": false, | |||||
"description": "This package name is not currently in use, but was formerly occupied by another package. To avoid malicious use, npm is hanging on to the package name, but loosely, and we'll probably give it to you if you want it.", | |||||
"homepage": "https://github.com/npm/security-holder#readme", | |||||
"keywords": [], | |||||
"license": "ISC", | |||||
"main": "index.js", | |||||
"name": "fs", | |||||
"repository": { | |||||
"type": "git", | |||||
"url": "git+https://github.com/npm/security-holder.git" | |||||
}, | |||||
"scripts": { | |||||
"test": "echo \"Error: no test specified\" && exit 1" | |||||
}, | |||||
"version": "0.0.1-security" | |||||
} |
{ | |||||
"name": "node-red-contrib-boolean-logic-ultimate", | |||||
"version": "0.0.1", | |||||
"lockfileVersion": 1, | |||||
"requires": true, | |||||
"dependencies": { | |||||
"fs": { | |||||
"version": "0.0.1-security", | |||||
"resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", | |||||
"integrity": "sha1-invTcYa23d84E/I4WLV+yq9eQdQ=" | |||||
} | |||||
} | |||||
} |
{ | { | ||||
"name": "node-red-contrib-boolean-logic", | |||||
"version": "0.0.3", | |||||
"description": "A set of Node-RED nodes for boolean logic", | |||||
"author": "Per Malmberg (https://github.com/PerMalmberg)", | |||||
"name": "node-red-contrib-boolean-logic-ultimate", | |||||
"version": "0.0.1", | |||||
"description": "A set of Node-RED enhanced boolean logic, with persisten values after reboot and more", | |||||
"author": "Supergiovane (https://github.com/Supergiovane)", | |||||
"dependencies": { | "dependencies": { | ||||
"fs": "0.0.1-security" | |||||
}, | |||||
"keywords": [ | |||||
"node-red", | |||||
"boolean", | |||||
"logic" | |||||
], | |||||
"license": "MIT", | |||||
"repository": { | |||||
"type": "git", | |||||
"url": "https://github.com/Supergiovane/node-red-contrib-boolean-logic-ultimate" | |||||
}, | }, | ||||
"keywords": [ "node-red", "boolean", "logic" ], | |||||
"license": "MIT", | |||||
"repository": { | |||||
"type": "git", | |||||
"url": "https://github.com/PerMalmberg/node-red-contrib-boolean-logic" | |||||
}, | |||||
"node-red": { | "node-red": { | ||||
"nodes": { | "nodes": { | ||||
"BooleanLogic": "boolean-logic/BooleanLogic.js", | |||||
"Invert": "boolean-logic/Invert.js", | |||||
"BDebug": "boolean-logic/BDebug.js" | |||||
"BooleanLogicUltimate": "boolean-logic-ultimate/BooleanLogicUltimate.js", | |||||
"Invert": "boolean-logic-ultimate/Invert.js", | |||||
"BDebug": "boolean-logic-ultimate/BDebug.js" | |||||
} | } | ||||
} | } | ||||
} | |||||
} |