UNPKG

node-red-contrib-timed-counter

Version:

A Node-RED node that counts messages received in a specified limit

176 lines (144 loc) 7.79 kB
module.exports = function (RED) { 'use strict'; function TimedCounter(config) { RED.nodes.createNode(this, config); var node = this; // Milliseconds for the timeout node.timelimit = parseInt(config.timelimit) * config.timeunit; // Should we only send the final message at the end of the // timeout, or send them all as they arrive but tagged with count? node.withhold = !!config.withhold; // Should we have a limited fixed timeout period for clicks to // accumulate, or do we keep counting until we stop clicking? node.fixedtimeout = !!config.fixedtimeout; // Should we count messages per topic, or just use a single // timer/count for all incoming messages? node.pertopic = !!config.pertopic; // Should we send the final message once a number of messages have // been received, if not the timeout first? node.countlimit = parseInt(config.countlimit); var TimedCounterHandler = function () { return { buffer: undefined, // The last message to be received, so the final message to be posted (if we're withholding) count: 0, // Number of clicks in the current run timeout: undefined, // The current timeout handle input: function (msg) { // Load configuration overrides from message, or use configuration as default. var timelimit = msg.timelimit !== undefined ? msg.timelimit : node.timelimit; var withhold = msg.withhold !== undefined ? msg.withhold : node.withhold; var fixedtimeout = msg.fixedtimeout !== undefined ? msg.fixedtimeout : node.fixedtimeout; var countlimit = msg.countlimit !== undefined ? msg.countlimit : node.countlimit; var handler = this; // If the message has a 'reset' property, reset the timer and the count. if (undefined !== msg['reset']) { msg.count = handler.count = 0; handler.buffer = undefined; // Clear any running timer if (undefined !== handler.timeout) { clearTimeout(handler.timeout); handler.timeout = undefined; } // This will NOT start the timer, as otherwise we'd // get the reset message passed through if no other // things arrive. If we're withholding, this means // the reset message will disappear; if we're not // withholding, then the reset message will flow // through. } else { // Normal message received (not a reset) // If we have no timeout, start the process... if (undefined === handler.timeout) { // Reset the count msg.count = handler.count = 0; } else if (!fixedtimeout) { // If fixedtimeout is false, then each time a message // is received within the timeout, the timeout is // reset. So, if the timeout is 2 seconds, and a // message is received every second, it'll keep // counting them until the messages stop. clearTimeout(handler.timeout); handler.timeout = undefined; } else { // Otherwise, it'll only count messages within the // fixed time limit once the timer is started. } // Increment the count handler.count++; // And store in the message msg.count = handler.count; if (!isNaN(countlimit) && countlimit > 0 && handler.count >= countlimit) { // If we have a count limit, and we've reached it, // then clear the timeout and send the message. // Clear any running timer if (undefined !== handler.timeout) { clearTimeout(handler.timeout); handler.timeout = undefined; } // and send the message node.send(msg); handler.buffer = undefined; } else if (undefined === handler.timeout) { // Now, if the timeout is unset (as earlier) or // the !fixedtimeout check has invalidated the // last one, then set it again. (If not, the // existing one should still be valid.) handler.timeout = setTimeout(function () { // First things first: clear the timeout // properly. handler.timeout = undefined; // Now, if we are withholding message(s) until // expiry, we need to finally dispatch. if (handler.buffer) { node.send(handler.buffer); handler.buffer = undefined; } }, timelimit); } } // At this point, we may have either a reset message with // count=0 and no timeout, or we'll have a real message // with a count > 0 and there should be a timeout running. if (withhold) { // If withholding, store the most recently received // message, and just send the last one when the timer // finally expires. handler.buffer = msg; } else { // If withhold is false, so send the messages tagged with // the count immediately as normal: this node will not // impede flow, but just tag the count value. node.send(msg); } } }; }; var handlers = []; var getHandler = function (topic) { // If there's no handler for this topic, create one if (undefined === handlers[topic]) { handlers[topic] = TimedCounterHandler(); } // and retrieve it regardless return handlers[topic]; }; // Handle an incoming message node.on('input', function (msg) { var handler; var pertopic = msg.pertopic !== undefined ? msg.pertopic : node.pertopic; if (pertopic) { // Use a handler for each topic handler = getHandler(msg.topic); } else { // Use a single handler for all messages handler = getHandler('all'); } return handler.input(msg); }); } RED.nodes.registerType("timed-counter", TimedCounter); }