node-red-contrib-boolean-logic-ultimate 1.0.63 → 1.1.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/CHANGELOG.md +4 -0
- package/README.md +1 -0
- package/boolean-logic-ultimate/BooleanLogicUltimate.html +12 -1
- package/boolean-logic-ultimate/BooleanLogicUltimate.js +488 -338
- package/boolean-logic-ultimate/Comparator.html +3 -2
- package/boolean-logic-ultimate/Comparator.js +1 -1
- package/boolean-logic-ultimate/FilterUltimate.html +15 -3
- package/boolean-logic-ultimate/FilterUltimate.js +67 -46
- package/boolean-logic-ultimate/InterruptFlowUltimate.html +14 -2
- package/boolean-logic-ultimate/InterruptFlowUltimate.js +155 -93
- package/boolean-logic-ultimate/InvertUltimate.html +13 -4
- package/boolean-logic-ultimate/InvertUltimate.js +74 -46
- package/boolean-logic-ultimate/RailwaySwitchUltimate.html +12 -1
- package/boolean-logic-ultimate/RailwaySwitchUltimate.js +110 -71
- package/boolean-logic-ultimate/toggleUltimate.html +12 -2
- package/boolean-logic-ultimate/toggleUltimate.js +1 -1
- package/boolean-logic-ultimate/translator-config.html +63 -0
- package/boolean-logic-ultimate/translator-config.js +8 -0
- package/boolean-logic-ultimate/utils.js +52 -43
- package/package.json +3 -2
|
@@ -1,339 +1,489 @@
|
|
|
1
1
|
module.exports = function (RED) {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
2
|
+
function BooleanLogicUltimate(config) {
|
|
3
|
+
RED.nodes.createNode(this, config);
|
|
4
|
+
var node = this;
|
|
5
|
+
var fs = require("fs");
|
|
6
|
+
var path = require("path");
|
|
7
|
+
|
|
8
|
+
node.config = config;
|
|
9
|
+
node.jSonStates = {}; // JSON object containing the states.
|
|
10
|
+
node.sInitializeWith =
|
|
11
|
+
typeof node.config.sInitializeWith === "undefined"
|
|
12
|
+
? "WaitForPayload"
|
|
13
|
+
: node.config.sInitializeWith;
|
|
14
|
+
node.persistPath = path.join(
|
|
15
|
+
RED.settings.userDir,
|
|
16
|
+
"booleanlogicultimatepersist"
|
|
17
|
+
); // 26/10/2020 Contains the path for the states dir.
|
|
18
|
+
node.restrictinputevaluation =
|
|
19
|
+
config.restrictinputevaluation === undefined
|
|
20
|
+
? false
|
|
21
|
+
: config.restrictinputevaluation;
|
|
22
|
+
node.delayEvaluation =
|
|
23
|
+
config.delayEvaluation === undefined ? 0 : config.delayEvaluation; // 26/01/2022 Starts evaluating the inputs only after this amount of time is elapsed, after the last msg input or trigger
|
|
24
|
+
if (
|
|
25
|
+
isNaN(parseInt(node.delayEvaluation)) ||
|
|
26
|
+
parseInt(node.delayEvaluation) < 0
|
|
27
|
+
)
|
|
28
|
+
node.delayEvaluation = 0;
|
|
29
|
+
if (typeof node.delayEvaluation === "string")
|
|
30
|
+
node.delayEvaluation = parseInt(node.delayEvaluation);
|
|
31
|
+
node.timerDelayEvaluation = null;
|
|
32
|
+
node.inputMessage = {}; // 26/01/2022 input message is stored here.
|
|
33
|
+
|
|
34
|
+
function setNodeStatus({ fill, shape, text }) {
|
|
35
|
+
let dDate = new Date();
|
|
36
|
+
node.status({
|
|
37
|
+
fill: fill,
|
|
38
|
+
shape: shape,
|
|
39
|
+
text:
|
|
40
|
+
text +
|
|
41
|
+
" (" +
|
|
42
|
+
dDate.getDate() +
|
|
43
|
+
", " +
|
|
44
|
+
dDate.toLocaleTimeString() +
|
|
45
|
+
")",
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Helper for the config html, to be able to delete the peristent states file
|
|
50
|
+
RED.httpAdmin.get(
|
|
51
|
+
"/stateoperation_delete",
|
|
52
|
+
RED.auth.needsPermission("BooleanLogicUltimate.read"),
|
|
53
|
+
function (req, res) {
|
|
54
|
+
//node.send({ req: req });
|
|
55
|
+
// Detele the persist file
|
|
56
|
+
//var _node = RED.nodes.getNode(req.query.nodeid); // Gets node object from nodeit, because when called from the config html, the node object is not defined
|
|
57
|
+
var _nodeid = req.query.nodeid;
|
|
58
|
+
try {
|
|
59
|
+
if (fs.existsSync(path.join(node.persistPath, _nodeid.toString())))
|
|
60
|
+
fs.unlinkSync(path.join(node.persistPath, _nodeid.toString()));
|
|
61
|
+
} catch (error) {}
|
|
62
|
+
res.json({ status: 220 });
|
|
63
|
+
}
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
// 26/10/2020 Check for path and create it if doens't exists
|
|
67
|
+
if (!fs.existsSync(node.persistPath)) {
|
|
68
|
+
// Create the path
|
|
69
|
+
try {
|
|
70
|
+
fs.mkdirSync(node.persistPath);
|
|
71
|
+
// Backward compatibility: Copy old states dir into the new folder
|
|
72
|
+
if (fs.existsSync("states")) {
|
|
73
|
+
var filenames = fs.readdirSync("states");
|
|
74
|
+
filenames.forEach((file) => {
|
|
75
|
+
RED.log.info(
|
|
76
|
+
"BooleanLogicUltimate: migrating from old states path to the new persist " +
|
|
77
|
+
file
|
|
78
|
+
);
|
|
79
|
+
fs.copyFileSync(
|
|
80
|
+
"states/" + file,
|
|
81
|
+
path.join(node.persistPath, path.basename(file))
|
|
82
|
+
);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
} catch (error) {
|
|
86
|
+
RED.log.error(
|
|
87
|
+
"BooleanLogicUltimate: error creating persistent folder. Check user permission to write to the filesystem " +
|
|
88
|
+
error.message
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Populate the state array with the persisten file
|
|
94
|
+
if (node.config.persist == true) {
|
|
95
|
+
try {
|
|
96
|
+
var contents = fs
|
|
97
|
+
.readFileSync(path.join(node.persistPath, node.id.toString()))
|
|
98
|
+
.toString();
|
|
99
|
+
if (typeof contents !== "undefined") {
|
|
100
|
+
node.jSonStates = JSON.parse(contents);
|
|
101
|
+
setNodeStatus({
|
|
102
|
+
fill: "blue",
|
|
103
|
+
shape: "ring",
|
|
104
|
+
text:
|
|
105
|
+
"Loaded persistent states (" +
|
|
106
|
+
Object.keys(node.jSonStates).length +
|
|
107
|
+
" total).",
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
} catch (error) {
|
|
111
|
+
setNodeStatus({
|
|
112
|
+
fill: "grey",
|
|
113
|
+
shape: "ring",
|
|
114
|
+
text: "No persistent states",
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
} else {
|
|
118
|
+
setNodeStatus({
|
|
119
|
+
fill: "yellow",
|
|
120
|
+
shape: "dot",
|
|
121
|
+
text: "Waiting for input states",
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Starts the evaluation delay timer, if needed
|
|
126
|
+
node.startTimerDelayEvaluation = () => {
|
|
127
|
+
if (node.timerDelayEvaluation !== null)
|
|
128
|
+
clearTimeout(node.timerDelayEvaluation);
|
|
129
|
+
node.timerDelayEvaluation = setTimeout(() => {
|
|
130
|
+
outputResult();
|
|
131
|
+
}, node.delayEvaluation);
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
// 14/08/2019 If some inputs are to be initialized, create a dummy items in the array
|
|
135
|
+
initUndefinedInputs();
|
|
136
|
+
|
|
137
|
+
this.on("input", function (msg) {
|
|
138
|
+
// 21/04/2021 Msg to reset all inputs
|
|
139
|
+
if (msg.hasOwnProperty("reset")) {
|
|
140
|
+
setNodeStatus({
|
|
141
|
+
fill: "blue",
|
|
142
|
+
shape: "ring",
|
|
143
|
+
text: "All inputs have been reset.",
|
|
144
|
+
});
|
|
145
|
+
node.jSonStates = [];
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// 26/01/2023 you can change the input count from msg
|
|
150
|
+
if (msg.hasOwnProperty("inputcount")) {
|
|
151
|
+
setTimeout(() => {
|
|
152
|
+
setNodeStatus({
|
|
153
|
+
fill: "grey",
|
|
154
|
+
shape: "dot",
|
|
155
|
+
text: "Input count changed to " + msg.inputcount,
|
|
156
|
+
});
|
|
157
|
+
}, 500);
|
|
158
|
+
try {
|
|
159
|
+
node.config.inputCount = Number(msg.inputcount);
|
|
160
|
+
} catch (error) {}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// 15/11/2021 inform user about undefined topic or payload
|
|
164
|
+
if (
|
|
165
|
+
!msg.hasOwnProperty("topic") ||
|
|
166
|
+
msg.topic === undefined ||
|
|
167
|
+
msg.topic === null
|
|
168
|
+
) {
|
|
169
|
+
setNodeStatus({
|
|
170
|
+
fill: "red",
|
|
171
|
+
shape: "dot",
|
|
172
|
+
text: "Received invalid topic!",
|
|
173
|
+
});
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
var topic = msg.topic;
|
|
178
|
+
const utils = require("./utils.js");
|
|
179
|
+
let sPayload = utils.fetchFromObject(
|
|
180
|
+
msg,
|
|
181
|
+
config.payloadPropName || "payload"
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
// 12/08/2021 Restrict only to true/false
|
|
185
|
+
if (node.restrictinputevaluation) {
|
|
186
|
+
if (sPayload !== true && sPayload !== false) {
|
|
187
|
+
setNodeStatus({
|
|
188
|
+
fill: "red",
|
|
189
|
+
shape: "dot",
|
|
190
|
+
text: "Received non boolean value from " + msg.topic,
|
|
191
|
+
});
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
var value = utils.ToBoolean(
|
|
197
|
+
sPayload,
|
|
198
|
+
RED.nodes.getNode(config.translatorConfig)// Retrieve the config node. It can be null, but it's handled in utils.js);
|
|
199
|
+
);
|
|
200
|
+
if (sPayload === undefined || value === undefined) {
|
|
201
|
+
// 15/11/2021 inform user about undefined topic or payload
|
|
202
|
+
setNodeStatus({
|
|
203
|
+
fill: "red",
|
|
204
|
+
shape: "dot",
|
|
205
|
+
text: "Received invalid payload from " + msg.topic || "",
|
|
206
|
+
});
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// 14/08/2019 if inputs are initialized, remove a "dummy" item from the state's array, as soon as new topic arrives
|
|
211
|
+
if (node.sInitializeWith !== "WaitForPayload") {
|
|
212
|
+
// Search if the current topic is in the state array
|
|
213
|
+
if (typeof node.jSonStates[topic] === "undefined") {
|
|
214
|
+
// Delete one dummy
|
|
215
|
+
for (let index = 0; index < node.config.inputCount; index++) {
|
|
216
|
+
if (node.jSonStates.hasOwnProperty("dummy" + index)) {
|
|
217
|
+
//RED.log.info(JSON.stringify(node.jSonStates))
|
|
218
|
+
delete node.jSonStates["dummy" + index];
|
|
219
|
+
//RED.log.info(JSON.stringify(node.jSonStates))
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Add current attribute
|
|
227
|
+
node.jSonStates[topic] = value;
|
|
228
|
+
|
|
229
|
+
// Save the state array to a perisistent file
|
|
230
|
+
if (node.config.persist == true) {
|
|
231
|
+
try {
|
|
232
|
+
fs.writeFileSync(
|
|
233
|
+
path.join(node.persistPath, node.id.toString()),
|
|
234
|
+
JSON.stringify(node.jSonStates)
|
|
235
|
+
);
|
|
236
|
+
} catch (error) {
|
|
237
|
+
setNodeStatus({
|
|
238
|
+
fill: "red",
|
|
239
|
+
shape: "dot",
|
|
240
|
+
text: "Node cannot write to filesystem: " + error.message,
|
|
241
|
+
});
|
|
242
|
+
RED.log.error(
|
|
243
|
+
"BooleanLogicUltimate: unable to write to the filesystem. Check wether the user running node-red, has write permission to the filesysten. " +
|
|
244
|
+
error.message
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
node.inputMessage = msg; // 26/01/2022 Store MSG to be used in the outputResult function.
|
|
249
|
+
|
|
250
|
+
// Do we have as many inputs as we expect?
|
|
251
|
+
var keyCount = Object.keys(node.jSonStates).length;
|
|
252
|
+
if (keyCount == node.config.inputCount) {
|
|
253
|
+
// var resAND = CalculateResult("AND");
|
|
254
|
+
// var resOR = CalculateResult("OR");
|
|
255
|
+
// var resXOR = CalculateResult("XOR");
|
|
256
|
+
|
|
257
|
+
// if (node.config.filtertrue == "onlytrue") {
|
|
258
|
+
// if (!resAND) { resAND = null };
|
|
259
|
+
// if (!resOR) { resOR = null };
|
|
260
|
+
// if (!resXOR) { resXOR = null };
|
|
261
|
+
// }
|
|
262
|
+
|
|
263
|
+
// Operation mode evaluation
|
|
264
|
+
if (node.config.outputtriggeredby == "onlyonetopic") {
|
|
265
|
+
if (
|
|
266
|
+
typeof node.config.triggertopic !== "undefined" &&
|
|
267
|
+
node.config.triggertopic !== "" &&
|
|
268
|
+
msg.hasOwnProperty("topic") &&
|
|
269
|
+
msg.topic !== "" &&
|
|
270
|
+
node.config.triggertopic === msg.topic
|
|
271
|
+
) {
|
|
272
|
+
if (node.delayEvaluation > 0) {
|
|
273
|
+
node.startTimerDelayEvaluation();
|
|
274
|
+
setNodeStatus({
|
|
275
|
+
fill: "blue",
|
|
276
|
+
shape: "ring",
|
|
277
|
+
text: "Delay Eval " + node.delayEvaluation + "ms",
|
|
278
|
+
});
|
|
279
|
+
} else {
|
|
280
|
+
outputResult();
|
|
281
|
+
}
|
|
282
|
+
} else {
|
|
283
|
+
setNodeStatus({
|
|
284
|
+
fill: "grey",
|
|
285
|
+
shape: "ring",
|
|
286
|
+
text:
|
|
287
|
+
"Saved (" +
|
|
288
|
+
(msg.hasOwnProperty("topic")
|
|
289
|
+
? msg.topic
|
|
290
|
+
: "empty input topic") +
|
|
291
|
+
") " +
|
|
292
|
+
value,
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
} else {
|
|
296
|
+
if (node.delayEvaluation > 0) {
|
|
297
|
+
node.startTimerDelayEvaluation();
|
|
298
|
+
setNodeStatus({
|
|
299
|
+
fill: "blue",
|
|
300
|
+
shape: "ring",
|
|
301
|
+
text: "Delay Eval " + node.delayEvaluation + "ms",
|
|
302
|
+
});
|
|
303
|
+
} else {
|
|
304
|
+
outputResult();
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
} else if (keyCount > node.config.inputCount) {
|
|
308
|
+
setNodeStatus({
|
|
309
|
+
fill: "gray",
|
|
310
|
+
shape: "ring",
|
|
311
|
+
text: "Reset due to unexpected new topic",
|
|
312
|
+
});
|
|
313
|
+
DeletePersistFile();
|
|
314
|
+
} else {
|
|
315
|
+
setNodeStatus({
|
|
316
|
+
fill: "green",
|
|
317
|
+
shape: "ring",
|
|
318
|
+
text: "Arrived topic " + keyCount + " of " + node.config.inputCount,
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
this.on("close", function (removed, done) {
|
|
324
|
+
if (removed) {
|
|
325
|
+
// This node has been deleted
|
|
326
|
+
// Delete persistent states on change/deploy
|
|
327
|
+
DeletePersistFile();
|
|
328
|
+
} else {
|
|
329
|
+
// This node is being restarted
|
|
330
|
+
}
|
|
331
|
+
done();
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
function DeletePersistFile() {
|
|
335
|
+
// Detele the persist file
|
|
336
|
+
try {
|
|
337
|
+
if (fs.existsSync(path.join(node.persistPath, node.id.toString())))
|
|
338
|
+
fs.unlinkSync(path.join(node.persistPath, node.id.toString()));
|
|
339
|
+
setNodeStatus({
|
|
340
|
+
fill: "red",
|
|
341
|
+
shape: "ring",
|
|
342
|
+
text: "Persistent states deleted (" + node.id.toString() + ").",
|
|
343
|
+
});
|
|
344
|
+
} catch (error) {
|
|
345
|
+
setNodeStatus({
|
|
346
|
+
fill: "red",
|
|
347
|
+
shape: "ring",
|
|
348
|
+
text: "Error deleting persistent file: " + error.toString(),
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
node.jSonStates = {}; // Resets inputs
|
|
352
|
+
// 14/08/2019 If the inputs are to be initialized, create a dummy items in the array
|
|
353
|
+
initUndefinedInputs();
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function initUndefinedInputs() {
|
|
357
|
+
if (node.sInitializeWith !== "WaitForPayload") {
|
|
358
|
+
var nTotalDummyToCreate =
|
|
359
|
+
Number(node.config.inputCount) - Object.keys(node.jSonStates).length;
|
|
360
|
+
if (nTotalDummyToCreate > 0) {
|
|
361
|
+
RED.log.info(
|
|
362
|
+
"BooleanLogicUltimate: Will create " +
|
|
363
|
+
nTotalDummyToCreate +
|
|
364
|
+
" dummy (" +
|
|
365
|
+
node.sInitializeWith +
|
|
366
|
+
") values"
|
|
367
|
+
);
|
|
368
|
+
for (let index = 0; index < nTotalDummyToCreate; index++) {
|
|
369
|
+
node.jSonStates["dummy" + index] =
|
|
370
|
+
node.sInitializeWith === "false" ? false : true;
|
|
371
|
+
}
|
|
372
|
+
let t = setTimeout(() => {
|
|
373
|
+
setNodeStatus({
|
|
374
|
+
fill: "green",
|
|
375
|
+
shape: "ring",
|
|
376
|
+
text:
|
|
377
|
+
"Initialized " +
|
|
378
|
+
nTotalDummyToCreate +
|
|
379
|
+
" undefined inputs with " +
|
|
380
|
+
node.sInitializeWith,
|
|
381
|
+
});
|
|
382
|
+
}, 4000);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function CalculateResult(_operation) {
|
|
388
|
+
var res;
|
|
389
|
+
|
|
390
|
+
if (_operation == "XOR") {
|
|
391
|
+
res = PerformXOR();
|
|
392
|
+
} else {
|
|
393
|
+
// We need a starting value to perform AND and OR operations.
|
|
394
|
+
var keys = Object.keys(node.jSonStates);
|
|
395
|
+
res = node.jSonStates[keys[0]];
|
|
396
|
+
|
|
397
|
+
for (var i = 1; i < keys.length; ++i) {
|
|
398
|
+
var key = keys[i];
|
|
399
|
+
res = PerformSimpleOperation(_operation, res, node.jSonStates[key]);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
return res;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
function PerformXOR() {
|
|
407
|
+
// XOR = exclusively one input is true. As such, we just count the number of true values and compare to 1.
|
|
408
|
+
var trueCount = 0;
|
|
409
|
+
|
|
410
|
+
for (var key in node.jSonStates) {
|
|
411
|
+
if (node.jSonStates[key]) {
|
|
412
|
+
trueCount++;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
return trueCount == 1;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function PerformSimpleOperation(operation, val1, val2) {
|
|
420
|
+
var res;
|
|
421
|
+
|
|
422
|
+
if (operation === "AND") {
|
|
423
|
+
res = val1 && val2;
|
|
424
|
+
} else if (operation === "OR") {
|
|
425
|
+
res = val1 || val2;
|
|
426
|
+
} else {
|
|
427
|
+
node.error("Unknown operation: " + operation);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
return res;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
function outputResult() {
|
|
434
|
+
let optionalTopic = node.config.topic;
|
|
435
|
+
let calculatedValueAND = CalculateResult("AND");
|
|
436
|
+
let calculatedValueOR = CalculateResult("OR");
|
|
437
|
+
let calculatedValueXOR = CalculateResult("XOR");
|
|
438
|
+
|
|
439
|
+
if (node.config.filtertrue == "onlytrue") {
|
|
440
|
+
if (!calculatedValueAND) {
|
|
441
|
+
calculatedValueAND = null;
|
|
442
|
+
}
|
|
443
|
+
if (!calculatedValueOR) {
|
|
444
|
+
calculatedValueOR = null;
|
|
445
|
+
}
|
|
446
|
+
if (!calculatedValueXOR) {
|
|
447
|
+
calculatedValueXOR = null;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
setNodeStatus({
|
|
452
|
+
fill: "green",
|
|
453
|
+
shape: "dot",
|
|
454
|
+
text:
|
|
455
|
+
"(AND)" +
|
|
456
|
+
(calculatedValueAND !== null ? calculatedValueAND : "---") +
|
|
457
|
+
" (OR)" +
|
|
458
|
+
(calculatedValueOR !== null ? calculatedValueOR : "---") +
|
|
459
|
+
" (XOR)" +
|
|
460
|
+
(calculatedValueXOR !== null ? calculatedValueXOR : "---"),
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
var msgAND = null;
|
|
464
|
+
if (calculatedValueAND != null) {
|
|
465
|
+
msgAND = RED.util.cloneMessage(node.inputMessage);
|
|
466
|
+
msgAND.topic = optionalTopic === undefined ? "result" : optionalTopic;
|
|
467
|
+
msgAND.operation = "AND";
|
|
468
|
+
msgAND.payload = calculatedValueAND;
|
|
469
|
+
}
|
|
470
|
+
var msgOR = null;
|
|
471
|
+
if (calculatedValueOR != null) {
|
|
472
|
+
msgOR = RED.util.cloneMessage(node.inputMessage);
|
|
473
|
+
msgOR.topic = optionalTopic === undefined ? "result" : optionalTopic;
|
|
474
|
+
msgOR.operation = "OR";
|
|
475
|
+
msgOR.payload = calculatedValueOR;
|
|
476
|
+
}
|
|
477
|
+
var msgXOR = null;
|
|
478
|
+
if (calculatedValueXOR != null) {
|
|
479
|
+
msgXOR = RED.util.cloneMessage(node.inputMessage);
|
|
480
|
+
msgXOR.topic = optionalTopic === undefined ? "result" : optionalTopic;
|
|
481
|
+
msgXOR.operation = "XOR";
|
|
482
|
+
msgXOR.payload = calculatedValueXOR;
|
|
483
|
+
}
|
|
484
|
+
node.send([msgAND, msgOR, msgXOR]);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
RED.nodes.registerType("BooleanLogicUltimate", BooleanLogicUltimate);
|
|
489
|
+
};
|