Massimo 5 лет назад
Родитель
Сommit
e1d7a11d59
5 измененных файлов: 98 добавлений и 35 удалений
  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 Просмотреть файл

# node-red-contrib-boolean-logic-ultimate # node-red-contrib-boolean-logic-ultimate
<p> <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/> <b>Version 1.0.3</b><br/>
- Node status: cosmetic adjustments<br/> - Node status: cosmetic adjustments<br/>
</p> </p>

+ 8
- 4
README.md Просмотреть файл

<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> <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> </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/> <br/><br/>



+ 27
- 1
boolean-logic-ultimate/BooleanLogicUltimate.html Просмотреть файл

}, },
filtertrue: {value:"both"}, filtertrue: {value:"both"},
persist: {value:true}, persist: {value:true},
sInitializeWith: {value:"WaitForPayload"},
triggertopic: { triggertopic: {
value: "trigger", value: "trigger",
required: false required: false
$("#node-input-outputtriggeredby").val("all"); $("#node-input-outputtriggeredby").val("all");
this.outputtriggeredby="all"; this.outputtriggeredby="all";
} }

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

}, },
oneditsave: function () { oneditsave: function () {
// Delete persistent state file // Delete persistent state file
<label for="node-input-outputtriggeredby"><i class="fa fa-filter"></i> Trigger mode</label> <label for="node-input-outputtriggeredby"><i class="fa fa-filter"></i> Trigger mode</label>
<select type="text" id="node-input-outputtriggeredby" placeholder="Event"> <select type="text" id="node-input-outputtriggeredby" placeholder="Event">
<option value="all">All topics</option> <option value="all">All topics</option>
<option value="onlyonetopic">Single topic + only eval others</option>
<option value="onlyonetopic">Single topic</option>
</select> </select>
</div> </div>
<div class="form-row" id="triggertopic"> <div class="form-row" id="triggertopic">
<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;"> <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 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>
<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> </script>


Every time you modify the node's config, <b>the retained values are cleared</b>.<br/> Every time you modify the node's config, <b>the retained values are cleared</b>.<br/>
<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): All incoming msg.payloads are converted into a boolean value according to the following rules (this applies to all boolean logic nodes):
<ol> <ol>
<li>Boolean values are taken as-is.</li> <li>Boolean values are taken as-is.</li>
</ol> </ol>
<br> <br>
The XOR operation operates in a one, and only one mode, i.e. (A ^ B) ^ C ... ^ n The XOR operation operates in a one, and only one mode, i.e. (A ^ B) ^ C ... ^ n

</p> </p>
</script> </script>

+ 58
- 29
boolean-logic-ultimate/BooleanLogicUltimate.js Просмотреть файл

module.exports = function(RED) { module.exports = function(RED) {
function BooleanLogicUltimate(config) { function BooleanLogicUltimate(config) {
RED.nodes.createNode(this,config);
this.config = config;
this.state = {};
RED.nodes.createNode(this, config);
var node = this; var node = this;
node.config = config; 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 fs = require('fs');
var decimal = /^\s*[+-]{0,1}\s*([\d]+(\.[\d]*)*)\s*$/ var decimal = /^\s*[+-]{0,1}\s*([\d]+(\.[\d]*)*)\s*$/
// Helper for the config html, to be able to delete the peristent states file // 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) { RED.httpAdmin.get("/stateoperation_delete", RED.auth.needsPermission('BooleanLogicUltimate.read'), function (req, res) {
//node.send({ req: req }); //node.send({ req: req });
try { try {
var contents = fs.readFileSync("states/" + node.id.toString()).toString(); var contents = fs.readFileSync("states/" + node.id.toString()).toString();
if (typeof contents !== 'undefined') { 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) { } catch (error) {
node.status({fill: "grey",shape: "ring",text: "No persistent states"}); node.status({fill: "grey",shape: "ring",text: "No persistent states"});
node.status({fill: "yellow",shape: "dot",text: "Waiting for input states"}); 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) { this.on('input', function (msg) {
var topic = msg.topic; var topic = msg.topic;
var payload = msg.payload; var payload = msg.payload;
if (topic !== undefined && payload !== undefined) { if (topic !== undefined && payload !== undefined) {
var value = ToBoolean( payload ); 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 // Save the state array to a perisistent file
if (this.config.persist == true) {
if (node.config.persist == true) {
try { try {
if (!fs.existsSync("states")) fs.mkdirSync("states"); 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) { } catch (error) {
node.status({fill: "red",shape: "dot",text: "Node cannot write to filesystem: " + error}); node.status({fill: "red",shape: "dot",text: "Node cannot write to filesystem: " + error});
} }
// Do we have as many inputs as we expect? // 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 ) { if( keyCount == node.config.inputCount ) {
? node.config.name : "BooleanLogicUltimate") ? node.config.name : "BooleanLogicUltimate")
+ " [Logic]: More than the specified " + " [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.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); DeletePersistFile(node.id);
DisplayUnkownStatus(); DisplayUnkownStatus();
} else { } else {
} catch (error) { } catch (error) {
_node.status({fill: "red",shape: "ring",text: "Error deleting persistent file: " + error.toString()}); _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) { function CalculateResult(_operation) {
} }
else { else {
// We need a starting value to perform AND and OR operations. // 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 ) { for( var i = 1; i < keys.length; ++i ) {
var key = keys[i]; var key = keys[i];
res = PerformSimpleOperation( _operation, res, node.state[key] );
res = PerformSimpleOperation( _operation, res, node.jSonStates[key] );
} }
} }
// XOR = exclusively one input is true. As such, we just count the number of true values and compare to 1. // XOR = exclusively one input is true. As such, we just count the number of true values and compare to 1.
var trueCount = 0; var trueCount = 0;
for( var key in node.state ) {
if( node.state[key] ) {
for( var key in node.jSonStates ) {
if( node.jSonStates[key] ) {
trueCount++; trueCount++;
} }
} }
return res; return res;
}; };
function DisplayStatus ( value ) {
node.status(
{
fill: value ? "green" : "red",
shape: value ? "dot" : "ring",
text: value ? "true" : "false"
}
);
};

function DisplayUnkownStatus () { function DisplayUnkownStatus () {
node.status( node.status(
{ {

+ 1
- 1
package.json Просмотреть файл

{ {
"name": "node-red-contrib-boolean-logic-ultimate", "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", "description": "A set of Node-RED enhanced boolean logic, with persisten values after reboot and more",
"author": "Supergiovane (https://github.com/Supergiovane)", "author": "Supergiovane (https://github.com/Supergiovane)",
"dependencies": { "dependencies": {

Загрузка…
Отмена
Сохранить