dockerized_openAger/nodered/rootfs/data/node_modules/node-red-contrib-msg-speed/msg_speed.js
2020-10-17 18:42:50 +02:00

176 lines
8.5 KiB
JavaScript

/**
* Copyright 2017 Bart Butenaers
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
module.exports = function(RED) {
"use strict";
var CircularBuffer = require("circular-buffer");
var Math = require("mathjs");
function speed(config) {
RED.nodes.createNode(this, config);
this.estimationStartup = config.estimation || false;
this.ignoreStartup = config.ignore || false;
// The buffer size depends on the frequency
switch(config.frequency) {
case 'sec':
this.bufferSize = 1;
break;
case 'min':
this.bufferSize = 60;
break;
case 'hour':
this.bufferSize = 60 * 60;
break;
default:
this.bufferSize = 1; // Default 'sec'
}
this.circularBuffer = new CircularBuffer(this.bufferSize);
this.frequency = config.frequency || 'sec';
this.msgCount = 0;
this.prevStartup = false;
this.prevTotalMsgCount = 0;
this.totalMsgCount = 0;
this.startTime = null; // When the first (unprocessed) message has arrived
this.endTime = null; // When the last (unprocessed) message has arrived
this.timer = null;
var analyse = function() {
if (node.startTime == null) {
node.startTime = new Date().getTime();
}
// Register the time when the last message has arrived (or when the last timer was called, when no message has arrived)
node.endTime = new Date().getTime();
// Calculate the time interval (in seconds) since the first (unprocessed) message has arrived
var seconds = (node.endTime - node.startTime) / 1000;
var remainder = (seconds - Math.floor(seconds)) * 1000;
seconds = Math.floor(seconds);
// Correct the end time with the remainder, since the time interval since the last message (until now) is skipped in the current calculation.
// Otherwise timeslices will get behind, and the curve would have some overshoot (at startup) period before reaching the final speed.
node.endTime -= remainder;
//console.log(seconds + " seconds between " + new Date(node.startTime).toISOString().slice(11, 23) + " and " + new Date(node.endTime).toISOString().slice(11, 23));
// Store the message count (of the previous second) in the circular buffer. However the timer can be
// delayed, so make sure this is done for EVERY second since the last time we got here ...
// 10 images/2,5 seconds = 240 images/second
for(var i = 0; i < seconds; i++) {
var original = node.totalMsgCount;
// In the first second we store (the count of) all received messages, except the last one (that has been received in the next second).
// In the next second we store that remaining (single) last message. In all later seconds (of this loop) we will store 0.
var added = (i == 0) ? Math.max(0, node.msgCount - 1) : Math.max(0, node.msgCount);
// Check the content of the tail buffer cell (before it is being removed by inserting a new cell at the head), if available already
var removed = (node.circularBuffer.size() >= node.bufferSize) ? node.circularBuffer.get(node.bufferSize-1) : 0;
// The total msg count is the sum of all message counts in the circular buffer. Instead of summing all those
// buffer cells continiously (over and over again), we will update the sum together with the buffer content.
// Sum = previous sum + message count of last second (which is going to be added to the buffer)
// - message count of the first second (which is going to be removed from the buffer).
node.totalMsgCount = original + added - removed;
//console.log(node.totalMsgCount + " = " + original + " + " + added + " - " + removed);
// Store the new count in the circular buffer (which will also trigger deletion of the oldest cell at the buffer trail)
node.circularBuffer.enq(added);
var totalMsgCount = node.totalMsgCount;
var startup = false;
// Do a linear interpolation if required (only relevant in the startup period)
if (node.circularBuffer.size() < node.circularBuffer.capacity()) {
startup = true;
if (node.estimationStartup == true && node.circularBuffer.size() > 0) {
totalMsgCount = Math.floor(totalMsgCount * node.circularBuffer.capacity() / node.circularBuffer.size());
}
}
// Update the status in the editor with the last message count (only if it has changed), or when switching between startup and real
if (node.prevTotalMsgCount != node.totalMsgCount || node.prevStartup != startup) {
// Show startup speed values in orange, and real values in green
if (startup == true) {
if (node.ignoreStartup == true) {
node.status({fill:"yellow",shape:"ring",text:" start ignored" });
}
else {
node.status({fill:"yellow",shape:"ring",text:" " + totalMsgCount + "/" + node.frequency });
}
}
else {
node.status({fill:"green",shape:"dot",text:" " + totalMsgCount + "/" + node.frequency });
}
node.prevTotalMsgCount = totalMsgCount;
}
// Send a message on the first output port, when not ignored during the startup period
if (node.ignoreStartup == false || startup == false) {
node.send([{ payload: totalMsgCount, frequency: node.frequency }, null]);
}
node.prevStartup = startup;
// The message count that has already been added, shouldn't be added again the next second
node.msgCount = Math.max(0, node.msgCount - added);
}
if (seconds > 0) {
// Our new second starts at the end of the previous second
node.startTime = node.endTime;
}
}
var node = this;
this.on("input", function(msg) {
if (node.timer) {
// An msg has arrived during the specified (timeout) interval, so remove the (timeout) timer.
clearInterval(node.timer);
}
node.msgCount += 1;
//console.log("New msg arrived resulting in a message count of " + node.msgCount );
analyse();
// Register a new timer (with a timeout interval of 1 second), in case no msg should arrive during the next second.
node.timer = setInterval(function() {
//console.log("Timer called. node.msgCount = " + node.msgCount );
// Seems no msg has arrived during the last second, so register a zero count
analyse();
}, 1000);
// Send the original message on the second output port
node.send([null, msg]);
});
this.on("close",function() {
if (node.timer) {
// Stop the previous timer, to avoid having multiple timers running in parallel after a redeploy.
clearInterval(node.timer);
}
node.status({});
});
}
RED.nodes.registerType("msg-speed", speed);
};