Massimo 5 år sedan
förälder
incheckning
e1d7a11d59
5 ändrade filer med 98 tillägg och 35 borttagningar
  1. +4
    -0
      CHANGELOG.md
  2. +8
    -4
      README.md
  3. +27
    -1
      boolean-logic-ultimate/BooleanLogicUltimate.html
  4. +58
    -29
      boolean-logic-ultimate/BooleanLogicUltimate.js
  5. +1
    -1
      package.json

+ 4
- 0
CHANGELOG.md Visa fil

@@ -1,5 +1,9 @@
# node-red-contrib-boolean-logic-ultimate
<p>
<b>Version 1.0.4</b><br/>
- Added the option to initialize the undefined inputs with true or false. Thanks to this, the node is immediately operative (will not wait until all topis arrives).<br/>
</p>
<p>
<b>Version 1.0.3</b><br/>
- Node status: cosmetic adjustments<br/>
</p>

+ 8
- 4
README.md Visa fil

@@ -51,10 +51,14 @@ Changing the topic is usually only needed when chaining multiple boolean nodes a
<li>Single topic + eval other inputs: <u>only whenever the node receives a msg input with the <b>specified topic</b> (having payload = true)</u>, it starts the evaluation of all other inputs as well and outputs the evaluated payload. If the node receives a msg input other than the <b>specified topic</b>), it only retains it's value for the boolean evaluation.</li>
</ol>

Example of trigger mode = Single topic + eval other inputs
```js
[{"id":"4d2e4d1.4a02034","type":"BooleanLogicUltimate","z":"5635003e.2d70f","name":"","filtertrue":"both","persist":true,"triggertopic":"MotionSensor","outputtriggeredby":"onlyonetopic","inputCount":"3","topic":"result","x":460,"y":580,"wires":[["a9e93fa0.99508"],[],[]]},{"id":"b3a4633e.ff06c","type":"inject","z":"5635003e.2d70f","name":"","topic":"MotionSensor","payload":"true","payloadType":"bool","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":110,"y":560,"wires":[["4d2e4d1.4a02034"]]},{"id":"150ff8fd.8110e7","type":"inject","z":"5635003e.2d70f","name":"","topic":"Dusk","payload":"true","payloadType":"bool","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":240,"y":760,"wires":[["4d2e4d1.4a02034"]]},{"id":"6ecd73e1.9ac75c","type":"inject","z":"5635003e.2d70f","name":"","topic":"Rain","payload":"true","payloadType":"bool","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":240,"y":660,"wires":[["4d2e4d1.4a02034"]]},{"id":"350fb477.b0d484","type":"inject","z":"5635003e.2d70f","name":"","topic":"Dusk","payload":"false","payloadType":"bool","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":240,"y":800,"wires":[["4d2e4d1.4a02034"]]},{"id":"1118f46a.b967ac","type":"inject","z":"5635003e.2d70f","name":"","topic":"MotionSensor","payload":"false","payloadType":"bool","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":110,"y":600,"wires":[["4d2e4d1.4a02034"]]},{"id":"4e793dec.646b4c","type":"inject","z":"5635003e.2d70f","name":"","topic":"Rain","payload":"false","payloadType":"bool","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":240,"y":700,"wires":[["4d2e4d1.4a02034"]]},{"id":"a9e93fa0.99508","type":"debug","z":"5635003e.2d70f","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":650,"y":580,"wires":[]},{"id":"8d44deef.e81d","type":"comment","z":"5635003e.2d70f","name":"Switch on the light only when it's raining and it's dusk. The trigger is someone entering in the Motion Sensor's area.","info":"Triggers only if someone enters \nin the motion sensor's area","x":410,"y":520,"wires":[]}]
```
<b>If input states are undefined</b><br />
Every time you create a node or modify the node, all inputs are set to undefined. This means that the node will wait the arrive of all topics (for example 3 topics, if you've selected 3 topics in the option), before it can output a payload. This can be a problem if your logic must be operative as soon as you deploy the flow. To overcome this problem, you can "initialize" all the undefined inputs with True or False.
<ol>
<li>Leave undefined: Standard behaviour, the node will wait all the "undefined" topics to arrive, then starts a flow with the result.</li>
<li>True or False: The node is immediately operative, by force the initialization of the "undefined" inputs with "true" or "false".</li>
</ol>
<br/>


<br/><br/>


+ 27
- 1
boolean-logic-ultimate/BooleanLogicUltimate.html Visa fil

@@ -8,6 +8,7 @@
},
filtertrue: {value:"both"},
persist: {value:true},
sInitializeWith: {value:"WaitForPayload"},
triggertopic: {
value: "trigger",
required: false
@@ -90,6 +91,14 @@
$("#node-input-outputtriggeredby").val("all");
this.outputtriggeredby="all";
}

// default
if(typeof this.sInitializeWith === "undefined")
{
$("#node-input-sInitializeWith").val("WaitForPayload");
this.sInitializeWith="WaitForPayload";
}

},
oneditsave: function () {
// Delete persistent state file
@@ -124,7 +133,7 @@
<label for="node-input-outputtriggeredby"><i class="fa fa-filter"></i> Trigger mode</label>
<select type="text" id="node-input-outputtriggeredby" placeholder="Event">
<option value="all">All topics</option>
<option value="onlyonetopic">Single topic + only eval others</option>
<option value="onlyonetopic">Single topic</option>
</select>
</div>
<div class="form-row" id="triggertopic">
@@ -136,6 +145,14 @@
<label style="width:auto" for="node-input-persist"> Remember latest input values after reboot</label> <input type="checkbox" id="node-input-persist" style="display:inline-block; width:auto; vertical-align:top;">
<div id="helpallga"><i> If checked, the input values are retained <br /> after a node-red reboot.<br/>If you reboot your node-red, <br />you don't need to wait all inputs <br />to arrive and initialize the node, <br />before the node can output a payload.</div>
</div>
<div class="form-row">
<label for="node-input-sInitializeWith"><i class="fa fa-home"></i> If input states are undefined</label>
<select type="text" id="node-input-sInitializeWith" placeholder="">
<option value="WaitForPayload">Leave undefined</option>
<option value="false">Initialize all with False</option>
<option value="true">Initialize all with True</option>
</select>
</div>

</script>

@@ -184,7 +201,15 @@
Every time you modify the node's config, <b>the retained values are cleared</b>.<br/>
<br/>
<b> If input states are undefined</b><br />
Every time you create a node or modify the node, all inputs are set to undefined. This means that the node will wait the arrive of all topics (for example 3 topics, if you've selected 3 topics in the option), before it can output a payload. This can be a problem if your logic must be operative as soon as you deploy the flow. To overcome this problem, you can "initialize" all the undefined inputs with True or False.
<ol>
<li>Leave undefined: Standard behaviour, the node will wait all the "undefined" topics to arrive, then starts a flow with the result.</li>
<li>True or False: The node is immediately operative, by force the initialization of the "undefined" inputs with "true" or "false".</li>
</ol>
<br/>

<bi How inputs are handled:</i><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>
@@ -193,5 +218,6 @@
</ol>
<br>
The XOR operation operates in a one, and only one mode, i.e. (A ^ B) ^ C ... ^ n

</p>
</script>

+ 58
- 29
boolean-logic-ultimate/BooleanLogicUltimate.js Visa fil

@@ -1,14 +1,13 @@
module.exports = function(RED) {
function BooleanLogicUltimate(config) {
RED.nodes.createNode(this,config);
this.config = config;
this.state = {};
RED.nodes.createNode(this, config);
var node = this;
node.config = config;
node.jSonStates = {}; // JSON object with elements. It's not an array! Format: {"Rain":true,"Dusk":true,"MotionSensor":true}
node.sInitializeWith = typeof node.config.sInitializeWith === "undefined" ? "WaitForPayload" : node.config.sInitializeWith;
var fs = require('fs');
var decimal = /^\s*[+-]{0,1}\s*([\d]+(\.[\d]*)*)\s*$/
// Helper for the config html, to be able to delete the peristent states file
RED.httpAdmin.get("/stateoperation_delete", RED.auth.needsPermission('BooleanLogicUltimate.read'), function (req, res) {
//node.send({ req: req });
@@ -21,8 +20,8 @@ module.exports = function(RED) {
try {
var contents = fs.readFileSync("states/" + 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(node.state).length + " total)."});
node.jSonStates = JSON.parse(contents);
node.status({fill: "blue",shape: "ring",text: "Loaded persistent states (" + Object.keys(node.jSonStates).length + " total)."});
}
} catch (error) {
node.status({fill: "grey",shape: "ring",text: "No persistent states"});
@@ -32,23 +31,45 @@ module.exports = function(RED) {
node.status({fill: "yellow",shape: "dot",text: "Waiting for input states"});
}


// 14/08/2019 If the inputs are to be initialized, create a dummy items in the array
initUndefinedInputs();

this.on('input', function (msg) {
var topic = msg.topic;
var payload = msg.payload;
if (topic !== undefined && payload !== undefined) {
var value = ToBoolean( payload );
var state = node.state;
// 14/08/2019 if inputs are initialized, remove a "dummy" item from the state's array, as soon as new topic arrives
if(node.sInitializeWith !== "WaitForPayload")
{
// Search if the current topic is in the state array
if (typeof node.jSonStates[topic] === "undefined")
{
// Delete one dummy
for (let index = 0; index < node.config.inputCount; index++) {
if (node.jSonStates.hasOwnProperty("dummy" + index)) {
//RED.log.info(JSON.stringify(node.jSonStates))
delete node.jSonStates["dummy" + index];
//RED.log.info(JSON.stringify(node.jSonStates))
break;
}
}
}
}
// Add current attribute
node.jSonStates[topic] = value;
state[topic] = value;

// Save the state array to a perisistent file
if (this.config.persist == true) {
if (node.config.persist == true) {
try {
if (!fs.existsSync("states")) fs.mkdirSync("states");
fs.writeFileSync("states/" + node.id.toString(),JSON.stringify(state));
fs.writeFileSync("states/" + node.id.toString(),JSON.stringify(node.jSonStates));
} catch (error) {
node.status({fill: "red",shape: "dot",text: "Node cannot write to filesystem: " + error});
@@ -56,7 +77,7 @@ module.exports = function(RED) {
}
// Do we have as many inputs as we expect?
var keyCount = Object.keys(state).length;
var keyCount = Object.keys(node.jSonStates).length;

if( keyCount == node.config.inputCount ) {
@@ -93,7 +114,7 @@ module.exports = function(RED) {
? 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 = {};
node.jSonStates = {};
DeletePersistFile(node.id);
DisplayUnkownStatus();
} else {
@@ -122,6 +143,23 @@ module.exports = function(RED) {
} catch (error) {
_node.status({fill: "red",shape: "ring",text: "Error deleting persistent file: " + error.toString()});
}
node.jSonStates = {}; // Resets inputs
// 14/08/2019 If the inputs are to be initialized, create a dummy items in the array
initUndefinedInputs();
}

function initUndefinedInputs() {
if (node.sInitializeWith !== "WaitForPayload")
{
var nTotalDummyToCreate = Number(node.config.inputCount) - Object.keys(node.jSonStates).length;
RED.log.info("BooleanLogicUltimate: Will create " + nTotalDummyToCreate + " dummy (" + node.sInitializeWith + ") values")
for (let index = 0; index < nTotalDummyToCreate; index++) {
node.jSonStates["dummy" + index] = node.sInitializeWith === "false" ? false : true;
}
if (nTotalDummyToCreate > 0) {
setTimeout(() => { node.status({fill: "green",shape: "ring",text: "Initialized " + nTotalDummyToCreate + " undefined inputs with " + node.sInitializeWith});}, 4000)
}
}
}

function CalculateResult(_operation) {
@@ -132,12 +170,12 @@ module.exports = function(RED) {
}
else {
// We need a starting value to perform AND and OR operations.
var keys = Object.keys(node.state);
res = node.state[keys[0]];
var keys = Object.keys(node.jSonStates);
res = node.jSonStates[keys[0]];
for( var i = 1; i < keys.length; ++i ) {
var key = keys[i];
res = PerformSimpleOperation( _operation, res, node.state[key] );
res = PerformSimpleOperation( _operation, res, node.jSonStates[key] );
}
}
@@ -149,8 +187,8 @@ module.exports = function(RED) {
// 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] ) {
for( var key in node.jSonStates ) {
if( node.jSonStates[key] ) {
trueCount++;
}
}
@@ -194,16 +232,7 @@ module.exports = function(RED) {
return res;
};
function DisplayStatus ( value ) {
node.status(
{
fill: value ? "green" : "red",
shape: value ? "dot" : "ring",
text: value ? "true" : "false"
}
);
};

function DisplayUnkownStatus () {
node.status(
{

+ 1
- 1
package.json Visa fil

@@ -1,6 +1,6 @@
{
"name": "node-red-contrib-boolean-logic-ultimate",
"version": "1.0.3",
"version": "1.0.4",
"description": "A set of Node-RED enhanced boolean logic, with persisten values after reboot and more",
"author": "Supergiovane (https://github.com/Supergiovane)",
"dependencies": {

Laddar…
Avbryt
Spara