├── README.md
├── icons
└── simple-message-queue.svg
├── package-lock.json
├── package.json
├── simple-message-queue.html
├── simple-message-queue.js
└── simple-message-queue.png
/README.md:
--------------------------------------------------------------------------------
1 | node-red-contrib-simple-message-queue
2 | ================================
3 |
4 | [](https://www.npmjs.com/package/node-red-contrib-simple-message-queue)
5 | [](https://www.npmjs.com/package/node-red-contrib-simple-message-queue)
6 |
7 | [](https://nodei.co/npm/node-red-contrib-simple-message-queue/)
8 |
9 | A simple queue node that store incoming messages in memory queue and uses a feedback from a following action or trigger message to release the next message stored in the queue.
10 |
11 | 
12 |
13 | # Inputs
14 | **trigger**: If a message is received with this property, one message from the queue will be released. The outbound message will have a _queueCount property with number of messages left in the queue. _queueCount will not include the message triggered. For example, if the message triggered is the last one in the queue, _queueCount will be 0 (zero).
15 |
16 | **reset**: If a message is received with the reset property, all messages in the queue will be cleared. In addition, the node will not forward the incoming reset message for processing by any subsequent nodes in the sequence.
17 |
18 | **ttl**: If a message is received with this property, a message will be added to the queue and will live in the queue for specific value in milliseconds. The value of the TTL must be a non-negative integer (0 <= n), describing the TTL period in milliseconds. Thus a value of 1000 means that a message added to the queue will live in the queue for 1 second. If message with TTL property has not been released for specified time, it will be deleted from queue without any output.
19 |
20 | **queueCount**: If a message is received with this property, node will send message with number of messages left in the queue in _queueCount property. This message won't store in queue.
21 |
22 | **bypass**: If a message is received with this property, and it set to true, bypass mode will turned on and all new incoming messages will be bypassed to output with _queueCount property, messaged in queue will not be cleared. If bypass property set to false, bypass mode will turned off and node return to normal operational, queue will not be cleared. Message with bypass property won't store in queue and trigger any messages.
23 |
24 | **bypassInterval**: Message with this property allow to dynamically change bypass interval in milliseconds from config, 0 - no bypass.
25 |
26 | ## Input hierarchy
27 | The following illustrates how the node will respond if more than one of the properties listed above is included in an incoming message.
28 | 1. **reset** -- the highest priority. if a message has a reset property, all other properties will be ignored.
29 | 2. **queueCount** -- will override trigger and bypass. The resulting outbound message will include the _queueCount property and the queueCount property will be removed. Any other properties on the incoming message will be ignored.
30 | 3. **bypassInterval** -- higher priority than queueCount. Message with this property won't store in queue and trigger any messages.
31 | 4. **bypass** -- higher priority than trigger. Message with bypass property won't store in queue and trigger any messages.
32 | 5. **trigger** -- one message from queue will be released as a result of a trigger message, it will include a _queueCount property. Message with trigger property won't store in queue.
33 |
34 | # Config
35 | **Bypass first message?**: If this flag set to True, first new message will be bypassed, than node will be busy until it's not get message with trigger property. True by default.
36 |
37 | **Bypass interval**: If this value greater than 0, node will realease message from queue without trigger after specific interval in milliseconds since last message send.
38 |
39 | This node is based on https://gist.github.com/dceejay/cea8afa28b7a93ebdc0f - respect to @dceejay (Dave Conway-Jones) https://github.com/dceejay
40 |
41 | # Example
42 | Here's a demo flow:
43 | `[{"id":"e089622.6a906a","type":"tab","label":"Flow 1"},{"id":"fe09fb63.f859e8","type":"inject","z":"e089622.6a906a","name":"","topic":"","payload":"test","payloadType":"str","repeat":"","crontab":"","once":false,"x":250,"y":160,"wires":[["6c1851d1.8b28f"]]},{"id":"cdb38d47.cd149","type":"debug","z":"e089622.6a906a","name":"","active":true,"console":"false","complete":"true","x":870,"y":160,"wires":[]},{"id":"4b9c998a.373cc8","type":"inject","z":"e089622.6a906a","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":240,"y":260,"wires":[["eda764e1.8074b8"]]},{"id":"eda764e1.8074b8","type":"change","z":"e089622.6a906a","name":"","rules":[{"t":"set","p":"trigger","pt":"msg","to":"1","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":440,"y":260,"wires":[["6c1851d1.8b28f"]]},{"id":"c5d04e3b.efe1f","type":"inject","z":"e089622.6a906a","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":240,"y":320,"wires":[["4f231841.69dd78"]]},{"id":"4f231841.69dd78","type":"change","z":"e089622.6a906a","name":"Bypass set to true","rules":[{"t":"set","p":"bypass","pt":"msg","to":"true","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":430,"y":320,"wires":[["6c1851d1.8b28f"]]},{"id":"49783823.727dd8","type":"inject","z":"e089622.6a906a","name":"","topic":"","payload":"test","payloadType":"str","repeat":"","crontab":"","once":false,"x":250,"y":120,"wires":[["b6103616.738bb8"]]},{"id":"b6103616.738bb8","type":"change","z":"e089622.6a906a","name":"","rules":[{"t":"set","p":"ttl","pt":"msg","to":"2000","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":430,"y":120,"wires":[["6c1851d1.8b28f"]]},{"id":"61c94eb4.f285e","type":"inject","z":"e089622.6a906a","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":240,"y":360,"wires":[["f80b3fd.988a6c"]]},{"id":"f80b3fd.988a6c","type":"change","z":"e089622.6a906a","name":"Bypass set to false","rules":[{"t":"set","p":"bypass","pt":"msg","to":"false","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":430,"y":360,"wires":[["6c1851d1.8b28f"]]},{"id":"86543cdf.3706c","type":"inject","z":"e089622.6a906a","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":240,"y":220,"wires":[["6d3ba29e.ea192c"]]},{"id":"6d3ba29e.ea192c","type":"change","z":"e089622.6a906a","name":"","rules":[{"t":"set","p":"reset","pt":"msg","to":"1","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":440,"y":220,"wires":[["6c1851d1.8b28f"]]},{"id":"5190f4e7.cabbbc","type":"inject","z":"e089622.6a906a","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":240,"y":80,"wires":[["61d34c47.cdfc94"]]},{"id":"61d34c47.cdfc94","type":"change","z":"e089622.6a906a","name":"","rules":[{"t":"set","p":"queueCount","pt":"msg","to":"","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":460,"y":80,"wires":[["6c1851d1.8b28f"]]},{"id":"6c1851d1.8b28f","type":"simple-queue","z":"e089622.6a906a","name":"","firstMessageBypass":false,"bypassInterval":"0","x":690,"y":160,"wires":[["cdb38d47.cd149"]]}]`
44 |
--------------------------------------------------------------------------------
/icons/simple-message-queue.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "node-red-contrib-simple-message-queue",
3 | "version": "0.2.7",
4 | "lockfileVersion": 2,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "node-red-contrib-simple-message-queue",
9 | "version": "0.2.7",
10 | "license": "Apache-2.0"
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "node-red-contrib-simple-message-queue",
3 | "version": "0.2.8",
4 | "description": "A simple queue node that store incoming messages in memory queue and uses a feedback from a following action or trigger message to release the next message stored in the queue.",
5 | "dependencies": { },
6 | "repository": {
7 | "type": "git",
8 | "url": "git://github.com/shady2k/node-red-contrib-simple-message-queue.git"
9 | },
10 | "keywords": [
11 | "node-red", "queue", "trigger", "store", "messages", "ttl"
12 | ],
13 | "node-red": {
14 | "nodes": {
15 | "simple-queue": "simple-message-queue.js"
16 | }
17 | },
18 | "bugs": {
19 | "url": "https://github.com/shady2k/node-red-contrib-simple-message-queue/issues"
20 | },
21 | "main": "simple-message-queue.js",
22 | "scripts": {
23 | "test": "echo \"Error: no test specified\" && exit 1"
24 | },
25 | "author": "shady2k ",
26 | "license": "Apache-2.0"
27 | }
28 |
--------------------------------------------------------------------------------
/simple-message-queue.html:
--------------------------------------------------------------------------------
1 |
15 |
16 |
36 |
37 |
51 |
52 |
--------------------------------------------------------------------------------
/simple-message-queue.js:
--------------------------------------------------------------------------------
1 | /*
2 | A simpe messages queue for IBM's Node-Red
3 | https://github.com/shady2k/node-red-contrib-simple-message-queue
4 | (c) 2017, shady2k
5 | Licensed under the Apache License, Version 2.0 (the "License");
6 | you may not use this file except in compliance with the License.
7 | You may obtain a copy of the License at
8 | http://www.apache.org/licenses/LICENSE-2.0
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 | */
15 |
16 | module.exports = function (RED) {
17 | function isNormalInteger(str) {
18 | return /^\+?(0|[1-9]\d*)$/.test(str);
19 | }
20 |
21 | function setBusyFalse(smq) {
22 | clearTimeout(smq.bypassTimer);
23 | smq.bypassTimer = null;
24 | smq.isBusy = false;
25 | }
26 |
27 | function setBusyTrue(smq) {
28 | smq.isBusy = true;
29 | }
30 |
31 | function bypassQueue(smq, context, node) {
32 | if (smq.bypassInterval > 0 && context.queue.length > 0 && !smq.bypassTimer) {
33 | smq.bypassTimer = setTimeout(function bypassSend() {
34 | smq.bypassTimer = null;
35 | if (context.queue.length > 0) {
36 | var m = context.queue.shift();
37 | m["_queueCount"] = context.queue.length;
38 | node.send(m);
39 |
40 | if (context.queue.length == 0) {
41 | smq.isBusy = false;
42 | }
43 |
44 | if (context.queue.length > 0) {
45 | smq.bypassTimer = setTimeout(bypassSend, smq.bypassInterval);
46 | }
47 |
48 | // Update status
49 | node.status({ fill: "green", shape: "ring", text: context.queue.length });
50 | } else {
51 | smq.bypassTimer = null;
52 | smq.isBusy = false;
53 | }
54 | }, smq.bypassInterval);
55 | }
56 | }
57 |
58 | function stopBypassTimer(smq) {
59 | clearTimeout(smq.bypassTimer);
60 | smq.bypassTimer = null;
61 | }
62 |
63 | function SimpleMessageQueueNode(config) {
64 |
65 | RED.nodes.createNode(this, config);
66 | var smq = {};
67 | smq.firstMessageBypass = config.firstMessageBypass || false;
68 | smq.bypassInterval = config.bypassInterval || 0;
69 | smq.isBusy = false;
70 | smq.bypassTimer = null;
71 | var node = this;
72 |
73 | // Yes it's true: an incoming message just happened
74 | this.on("input", function (msg) {
75 | var now = Date.now;
76 | var context = node.context();
77 |
78 | // if queue doesn't exist, create it
79 | context.queue = context.queue || [];
80 | context.is_disabled = context.is_disabled || false;
81 |
82 | // if the msg is a reset, clear queue
83 | if (msg.hasOwnProperty("reset")) {
84 | context.queue = [];
85 | setBusyFalse(smq);
86 | } else if (msg.hasOwnProperty("queueCount")) {
87 | msg["_queueCount"] = context.queue.length;
88 | node.send(msg);
89 | } else if (msg.hasOwnProperty("bypassInterval")) {
90 | let re = /^\+?(0|[1-9]\d*)$/;
91 | if (re.test(msg.bypassInterval)) {
92 | smq.bypassInterval = msg.bypassInterval;
93 | }
94 | } else if (msg.hasOwnProperty("bypass")) {
95 | if (msg.bypass) {
96 | context.is_disabled = true;
97 | } else {
98 | context.is_disabled = false;
99 | setBusyFalse(smq);
100 | //context.queue = [];
101 | }
102 | } else if (msg.hasOwnProperty("trigger")) { // if the msg is a trigger one release next message
103 | // Filter overdue messages
104 | context.queue = context.queue.filter(function (x) {
105 | return ((now() - x._queuetimestamp) < x.ttl || x.ttl == 0);
106 | });
107 | if (context.queue.length > 0) {
108 | var m = context.queue.shift();
109 | m["_queueCount"] = context.queue.length;
110 | node.send(m);
111 | stopBypassTimer(smq);
112 | bypassQueue(smq, context, node);
113 | } else {
114 | setBusyFalse(smq);
115 | }
116 | } else {
117 | if (context.is_disabled || (smq.firstMessageBypass && !smq.isBusy)) {
118 | setBusyTrue(smq);
119 | msg["_queueCount"] = context.queue.length;
120 | node.send(msg);
121 | stopBypassTimer(smq);
122 | bypassQueue(smq, context, node);
123 | } else {
124 | // Check if ttl value of new message is positive integer
125 | var ttl = msg.ttl || 0;
126 | if (!isNormalInteger(ttl)) ttl = 0;
127 |
128 | msg.ttl = ttl;
129 | msg._queuetimestamp = now();
130 | context.queue.push(msg); // Add to queue
131 |
132 | // Filter overdue messages
133 | context.queue = context.queue.filter(function (x) {
134 | return ((now() - x._queuetimestamp) < x.ttl || x.ttl == 0);
135 | });
136 | }
137 | }
138 |
139 | bypassQueue(smq, context, node);
140 | // Update status
141 | node.status({ fill: "green", shape: "ring", text: context.queue.length });
142 | });
143 |
144 | this.on("close", function () {
145 | // Update status
146 | node.status({ fill: "green", shape: "ring", text: 0 });
147 | });
148 | }
149 |
150 | RED.nodes.registerType("simple-queue", SimpleMessageQueueNode);
151 | };
152 |
--------------------------------------------------------------------------------
/simple-message-queue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shady2k/node-red-contrib-simple-message-queue/03074e1cb49dd06fd001b05abe95c7930b9d0985/simple-message-queue.png
--------------------------------------------------------------------------------