Delay node enhancements (#2294)

* Remove unused messages in message catalog

* Support msg.rate in delay node

* Support nodeMessageBufferMaxLength in delay node

* Add logging function for queue size

* Support msg.nodeMessageBufferMaxLength

* Revert "Support msg.nodeMessageBufferMaxLength"

This reverts commit cc72f892f7.

* Improve logging function for delay node

* Add support for Messaging API to delay node

* Add documentation about msg.rate in delay node

* Add test cases for msg.rate in delay node

Co-authored-by: Dave Conway-Jones <dceejay@users.noreply.github.com>
This commit is contained in:
Kazuhito Yokoi 2021-04-22 17:01:28 +09:00 committed by GitHub
parent 719aea2a58
commit a20049c82a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 686 additions and 52 deletions

View File

@ -20,6 +20,20 @@ module.exports = function(RED) {
var MILLIS_TO_NANOS = 1000000;
var SECONDS_TO_NANOS = 1000000000;
var _maxKeptMsgsCount;
function maxKeptMsgsCount(node) {
if (_maxKeptMsgsCount === undefined) {
var name = "nodeMessageBufferMaxLength";
if (RED.settings.hasOwnProperty(name)) {
_maxKeptMsgsCount = RED.settings[name];
}
else {
_maxKeptMsgsCount = 0;
}
}
return _maxKeptMsgsCount;
}
function DelayNode(n) {
RED.nodes.createNode(this,n);
@ -78,6 +92,7 @@ module.exports = function(RED) {
this.randomID = -1;
this.lastSent = null;
this.drop = n.drop;
this.droppedMsgs = 0;
var node = this;
function ourTimeout(handler, delay, clearHandler) {
@ -88,6 +103,19 @@ module.exports = function(RED) {
};
}
var sendMsgFromBuffer = function() {
if (node.buffer.length === 0) {
clearInterval(node.intervalID);
node.intervalID = -1;
}
if (node.buffer.length > 0) {
const msgInfo = node.buffer.shift();
msgInfo.send(msgInfo.msg);
msgInfo.done();
}
node.reportDepth();
}
var clearDelayList = function(s) {
for (var i=0; i<node.idList.length; i++ ) { node.idList[i].clear(); }
node.idList = [];
@ -112,6 +140,14 @@ module.exports = function(RED) {
}
}
var loggerId = setInterval(function () {
if (node.droppedMsgs !== 0) {
node.debug("node.droppedMsgs = " + node.droppedMsgs);
node.droppedMsgs = 0;
}
}, 15 * 1000);
node.on("close", function() { clearInterval(loggerId); });
if (node.pauseType === "delay") {
node.on("input", function(msg, send, done) {
if (msg.hasOwnProperty("flush")) { flushDelayList(); done(); }
@ -166,28 +202,32 @@ module.exports = function(RED) {
done();
return;
}
if (!node.drop) {
var m = RED.util.cloneMessage(msg);
delete m.flush;
if (node.intervalID !== -1) {
node.buffer.push({msg: m, send: send, done: done});
node.reportDepth();
if (msg.hasOwnProperty("rate") && !isNaN(parseFloat(msg.rate)) && node.rate !== msg.rate) {
node.rate = msg.rate;
clearInterval(node.intervalID);
node.intervalID = setInterval(sendMsgFromBuffer, node.rate);
}
var max_msgs = maxKeptMsgsCount(node);
if ((max_msgs > 0) && (node.buffer.length >= max_msgs)) {
node.buffer = [];
node.error(RED._("delay.errors.too-many"), msg);
} else {
node.buffer.push({msg: m, send: send, done: done});
node.reportDepth();
}
}
else {
if (msg.hasOwnProperty("rate") && !isNaN(parseFloat(msg.rate))) {
node.rate = msg.rate;
}
send(m);
node.reportDepth();
node.intervalID = setInterval(function() {
if (node.buffer.length === 0) {
clearInterval(node.intervalID);
node.intervalID = -1;
}
if (node.buffer.length > 0) {
const msgInfo = node.buffer.shift();
msgInfo.send(msgInfo.msg);
msgInfo.done();
}
node.reportDepth();
}, node.rate);
node.intervalID = setInterval(sendMsgFromBuffer, node.rate);
done();
}
if (msg.hasOwnProperty("flush")) {
@ -201,17 +241,40 @@ module.exports = function(RED) {
}
}
else {
var timeSinceLast;
if (node.lastSent) {
timeSinceLast = process.hrtime(node.lastSent);
}
if (!node.lastSent) { // ensuring that we always send the first message
node.lastSent = process.hrtime();
send(msg);
}
else if ( ( (timeSinceLast[0] * SECONDS_TO_NANOS) + timeSinceLast[1] ) > (node.rate * MILLIS_TO_NANOS) ) {
node.lastSent = process.hrtime();
send(msg);
if (maxKeptMsgsCount(node) > 0) {
if (node.intervalID === -1) {
node.send(msg);
node.intervalID = setInterval(sendMsgFromBuffer, node.rate);
} else {
if (msg.hasOwnProperty("rate") && node.rate !== msg.rate) {
node.rate = msg.rate;
clearInterval(node.intervalID);
node.intervalID = setInterval(sendMsgFromBuffer, node.rate);
}
if (node.buffer.length < _maxKeptMsgsCount) {
var m = RED.util.cloneMessage(msg);
node.buffer.push({msg: m, send: send, done: done});
} else {
node.trace("dropped due to buffer overflow. msg._msgid = " + msg._msgid);
node.droppedMsgs++;
}
}
} else {
if (msg.hasOwnProperty("rate")) {
node.rate = msg.rate;
}
var timeSinceLast;
if (node.lastSent) {
timeSinceLast = process.hrtime(node.lastSent);
}
if (!node.lastSent) { // ensuring that we always send the first message
node.lastSent = process.hrtime();
send(msg);
}
else if ( ( (timeSinceLast[0] * SECONDS_TO_NANOS) + timeSinceLast[1] ) > (node.rate * MILLIS_TO_NANOS) ) {
node.lastSent = process.hrtime();
send(msg);
}
}
done();
}

View File

@ -29,6 +29,567 @@
"errors": {
"nooverride": "Warnung: Nachrichten-Eigenschaften können die Eigenschaften des festgelegten Nodes nicht mehr außer Kraft setzen. Siehe Bit.ly/nr-override-msg-props"
}
}
}
},
"trigger" : {
"send" : "Senden",
"then" : "dann",
"then-send" : "dann senden",
"output" : {
"string" : "die Zeichenfolge",
"number" : "die Zahl",
"existing" : "das vorhandene Nachrichtenobjekt",
"original" : "das ursprüngliche Nachrichtenobjekt",
"latest" : "das neueste Nachrichtenobjekt",
"nothing" : "nichts"
},
"wait-reset" : "warten auf Zurücksetzen",
"wait-for" : "warten auf",
"wait-loop" : "erneut senden aller",
"for" : "Handhabung",
"bytopics" : "für jeden <code>msg.topic</code> Wert unabhängig",
"alltopics" : "alle Nachrichten",
"duration" : {
"ms" : "Millisek.",
"s" : "Sekunden",
"m" : "Minuten",
"h" : "Stunden"
},
"extend" : " Verlängerung der Verzögerung bei Eingang neuer Nachricht",
"label" : {
"trigger" : "Auslöser",
"trigger-block" : "Auslöser & Block",
"trigger-loop" : "alle erneut senden",
"reset" : "Setzen Sie den Auslöser zurück, wenn:",
"resetMessage" : "msg.reset gesetzt ist oder ",
"resetPayload" : "msg.payload ist gleich" ,
"resetprompt" : "optional"
}
},
"comment" : {
"comment" : "Kommentar"
},
"unknown" : {
"label" : {
"unknown" : "unbekannt"
},
"tip" : "<p> Dieser Node ist ein Typ, der Ihrer Installation von Node-RED unbekannt ist. </p> <p> <i> Wenn Sie mit dem Node in diesem Status deployen, wird die Konfiguration beibehalten, aber der Fluss wird erst gestartet, wenn der fehlende Typ installiert ist. </i> </p> <p> Weitere Informationen finden Sie in der Info-Seitenleiste für. Weitere Hilfe </p>"
},
"mqtt" : {
"label" : {
"broker" : "Server",
"example" : "z. B. lokaler_Host",
"qos" : "QoS",
"retain" : "Retain",
"clientid" : "Client-ID",
"port" : "Port",
"keepalive" : "Keepalive-Zeit (en)",
"cleansession" : "Bereinigte Sitzung verwenden",
"use-tls" : "Sichere Verbindung (SSL/TLS) aktivieren",
"tls-config" : "TLS-Konfiguration",
"verify-server-cert" : "Serverzertifikat überprüfen",
"compatmode" : "Traditionelle MQTT 3.1-Unterstützung verwenden"
},
"sections-label" : {
"birth-message" : "Nachricht über Verbindungsaufbau ",
"will-message" : "Nachricht über unerwarteten Abschaltung",
"close-message" : "Nachricht bevor die Verbindung beendet wird"
},
"tabs-label" : {
"connection" : "Verbindung",
"security" : "Sicherheit",
"messages" : "Nachrichten"
},
"placeholder" : {
"clientid" : "Leerer Wert für automatische Generierung",
"clientid-nonclean" : "Muss für nicht bereinigte Sitzungen festgelegt werden.",
"will-topic" : "inaktivieren wenn leer",
"birth-topic" : "inaktivieren wenn leer",
"close-topic" : "inaktivieren wenn leer"
},
"state" : {
"connected" : "Verbindung zum Broker __broker__ hergestellt.",
"disconnected" : "Verbindung zum Broker __broker__ wurde beendet.",
"connect-failed" : "Verbindung zum Broker __broker__ konnte nicht hergestellt werden."
},
"retain" : "Retain",
"true" : "Wahr",
"false" : "Falsch",
"tip" : "Tipp: Behalten Sie das Topic \"Artikel\", \"qos\" oder \"retain\" bei, wenn Sie diese über die Eigenschaft \"msg\" festlegen",
"errors" : {
"not-defined" : "Topic nicht definiert",
"missing-config" : "Fehlende Brokerkonfiguration",
"invalid-topic" : "Ungültiges Topic angegeben",
"nonclean-missingclientid" : "Keine Client-ID-Gruppe unter Verwendung einer bereinigten Sitzung"
}
},
"httpin" : {
"label" : {
"method" : "Methode",
"url" : "URL",
"doc" : "Docs",
"return" : "Rückgabe",
"upload" : "Dateiuploads akzeptieren?",
"status" : "Statuscode",
"headers" : "Kopfzeilen",
"other" : "andere"
},
"setby" : "-durch msg.method festgelegt-",
"basicauth" : "Basisauthentifizierung verwenden",
"use-tls" : "Sichere Verbindung (SSL/TLS) aktivieren",
"tls-config" : "TLS-Konfiguration",
"utf8" : "eine UTF-8-Zeichenfolge",
"binary" : "einen binären Buffer",
"json" : "ein analysiertes JSON-Objekt",
"tip" : {
"in" : "Die URL ist relativ zu ",
"res" : "Die an diesen Node gesendeten Nachrichten <b> müssen </b> von einem <i> http-Input </i> -Node stammen",
"req" : "Tipp: Wenn die JSON-Syntaxanalyse fehlschlägt, wird die abgerufene Zeichenfolge als-ist zurückgegeben."
},
"httpreq" : "HTTP-Anforderung",
"errors" : {
"not-created" : "http-in-Node kann nicht erstellt werden, wenn httpNodeRoot auf 'false' gesetzt ist.",
"missing-path" : "Fehlendes Pfad",
"no-response" : "Kein Antwortobjekt",
"json-error" : "JSON-Parsing-Fehler",
"no-url" : "Keine URL angegeben",
"deprecated-call" : "Nicht weiter unterstützter Aufruf von __method__",
"invalid-transport" : "Nicht-http-Transport angefordert"
},
"status" : {
"requesting" : "anfordern"
}
},
"websocket" : {
"label" : {
"type" : "Typ",
"path" : "Pfad",
"url" : "URL"
},
"listenon" : "Empfangsbereit",
"connectto" : "Verbinden mit",
"sendrec" : "Senden/Empfangen",
"payload" : "Nutzdaten",
"message" : "gesamte Nachricht",
"tip" : {
"path1" : "Standardmäßig enthält <code> Nutzdaten </code> die Daten, die über einen Websocket gesendet oder von einem Websocket empfangen werden. Der Listener kann so konfiguriert werden, dass er das gesamte Nachrichtenobjekt als eine JSON-formatierte Zeichenfolge sendet oder empfängt.",
"path2" : "Dieser Pfad ist relativ zu <code>__path__</code>.",
"url1" : "URL sollte ws: &#47; & #47; oder wss: &#47; & #47; Schema verwenden und auf einen vorhandenen Websocket-Listener verweisen.",
"url2" : "Standardmäßig enthält <code> Nutzdaten </code> die Daten, die über einen Websocket gesendet oder von einem Websocket empfangen werden. Der Client kann so konfiguriert werden, dass er das gesamte Nachrichtenobjekt als eine JSON-formatierte Zeichenfolge sendet oder empfängt."
},
"status" : {
"connected" : "verbunden __count__",
"connected_plural" : "verbunden __count__"
},
"errors" : {
"connect-error" : "Bei der WS-Verbindung ist ein Fehler aufgetreten: ",
"send-error" : "Beim Senden ist ein Fehler aufgetreten: ",
"missing-conf" : "Fehlende Serverkonfiguration"
}
},
"watch" : {
"watch" : "Überwachung",
"label" : {
"files" : "Datei (en)",
"recursive" : "Unterverzeichnisse rekursiv überwachen"
},
"placeholder" : {
"files" : "Durch Komma getrennte Liste von Dateien und/oder Verzeichnissen"
},
"tip" : "Unter Windows müssen Sie doppelte Backslashes \\\\ in beliebigen Verzeichnisnamen verwenden."
},
"tcpin" : {
"label" : {
"type" : "Typ",
"output" : "Ausgabe",
"port" : "Port",
"host" : "auf Host",
"payload" : "Payload (en)",
"delimited" : "begrenzt durch",
"close-connection" : "Schließen Sie die Verbindung, nachdem jede Nachricht gesendet wurde?",
"decode-base64" : "Nachricht aus Base64 dekodierien?",
"server" : "Server",
"return" : "Rückgabe",
"ms" : "ms",
"chars" : "Chars"
},
"type" : {
"listen" : "Empfangsbereit",
"connect" : "Verbinden mit",
"reply" : "Auf TCP antworten"
},
"output" : {
"stream" : "Datenstrom von",
"single" : "Single",
"buffer" : "Buffer",
"string" : "Zeichenfolge",
"base64" : "Base64-Zeichenfolge"
},
"return" : {
"timeout" : "nach einem festen Zeitlimit von",
"character" : "wenn folgendes Zeichen empfangen wird",
"number" : "eine festgelegte Anzahl von Zeichen",
"never" : "keine Rückgabe - Verbindung wird offen gehalten",
"immed" : "sofort - Wartet nicht auf Antwort."
},
"status" : {
"connecting" : "Verbindung zu __host__: __port__",
"connected" : "Verbindung zu __host__: __port__",
"listening-port" : "empfangsbereit an Port __port__",
"stopped-listening" : "Empfangsbereitschaft an Port gestoppt",
"connection-from" : "Verbindung von __host__: __port__",
"connection-closed" : "Verbindung geschlossen von __host__: __port__",
"connections" : "__count__connection",
"connections_plural" : "__count__connections"
},
"errors" : {
"connection-lost" : "Verbindung verloren zu __host__: __port__",
"timeout" : "Zeitlimit für geschlossenen Socket-Port __port__",
"cannot-listen" : "Port __port__ kann nicht empfangsbereit sein. Fehler: __error__",
"error" : "Fehler: __error__",
"socket-error" : "Socketfehler von __host__: __port__",
"no-host" : "Host und/oder Port nicht festgelegt",
"connect-timeout" : "Verbindungszeitlimit",
"connect-fail" : "Verbindung fehlgeschlagen"
}
},
"udp" : {
"label" : {
"listen" : "Empfangsbereit",
"onport" : "an Port",
"using" : "verwenden",
"output" : "Ausgabe",
"group" : "Gruppe",
"interface" : "Lokal IF",
"send" : "Schicken Sie eine",
"toport" : "an Port",
"address" : "Adresse",
"decode-base64" : "Dekodiere Base64-kodierte Nutzdaten?"
},
"placeholder" : {
"interface" : "(optional) lokale Schnittstelle oder Adresse, an die gebunden werden soll",
"interfaceprompt" : "(optional) lokale Schnittstelle oder Adresse, an die gebunden werden soll",
"address" : "Ziel-IP"
},
"udpmsgs" : "udp-Nachrichten",
"mcmsgs" : "Multicastnachrichten",
"udpmsg" : "udp-Nachricht",
"bcmsg" : "Broadcastnachricht",
"mcmsg" : "Multicastnachricht",
"output" : {
"buffer" : "ein Buffer",
"string" : "eine Zeichenfolge",
"base64" : "Eine Base64-codierte Zeichenfolge"
},
"bind" : {
"random" : "an zufälliger lokaler Port binden",
"local" : "Bindung an lokalen Port",
"target" : "Bindung an Zielport"
},
"tip" : {
"in" : "Tipp: Stellen Sie sicher, dass Ihre Firewall die Daten in zulässt.",
"out" : "Tipp: Lassen Sie die Adresse und den Port leer, wenn Sie mit <code> msg.ip </code> und <code> msg.port </code> festlegen möchten.",
"port" : "Ports, die bereits verwendet werden: "
},
"status" : {
"listener-at" : "udp listener at __host__: __port__",
"mc-group" : "udp Multicastgruppe __Gruppe__",
"listener-stopped" : "udp-Listener gestoppt",
"output-stopped" : "udp-Ausgabe gestoppt",
"mc-ready" : "udp multicast ready: __iface__: __outport__-> __host__: __port__",
"bc-ready" : "udp broadcast ready: __outport__-> __host__: __port__",
"ready" : "udp ready: __outport__-> __host__: __port__",
"ready-nolocal" : "udp bereit: __host__: __port__",
"re-use" : "udp re-use socket: __outport__-> __host__: __port__"
},
"errors" : {
"access-error" : "UDP-Zugriffsfehler, Sie benötigen möglicherweise Rootzugriff für Ports unter 1024",
"error" : "Fehler: __error__",
"bad-mcaddress" : "Ungültige Multicastadresse",
"interface" : "Sie müssen die IP-Adresse der erforderlichen Schnittstelle sein.",
"ip-notset" : "udp: ip-Adresse nicht festgelegt",
"port-notset" : "udp: Port nicht festgelegt",
"port-invalid" : "udp: Portnummer nicht gültig",
"alreadyused" : "udp: Port __port__ wird bereits verwendet",
"ifnotfound" : "udp: interface __iface__ nicht gefunden"
}
},
"switch" : {
"switch" : "Switch",
"label" : {
"property" : "Eigenschaft",
"rule" : "Regel",
"repair" : "Nachrichtenfolgen erneut erstellen"
},
"and" : "und",
"checkall" : "Alle Regeln überprüfen",
"stopfirst" : "Nach erster Übereinstimmung stoppen",
"ignorecase" : "Groß-/Kleinschreibung ignorieren",
"rules" : {
"btwn" : "liegt zwischen",
"cont" : "enthält",
"regex" : "Übereinstimmungen mit regex",
"true" : "ist wahr",
"false" : "ist falsch",
"null" : "ist null",
"nnull" : "ist nicht null",
"istype" : "ist vom Typ",
"empty" : "ist leer",
"nempty" : "ist nicht leer",
"head" : "Header",
"tail" : "Trailer",
"index" : "Index zwischen",
"exp" : "JSONata Ausdruck",
"else" : "Andernfalls"
},
"errors" : {
"invalid-expr" : "Ungültiger JSONata Ausdruck: __error__",
"too-many" : "Zu viele anstehende Nachrichten im Switch-Node"
}
},
"change" : {
"label" : {
"rules" : "Regeln",
"rule" : "Regel",
"set" : "setze __property__",
"change" : "__property__ ändern",
"delete" : "__property__ löschen",
"move" : "bewege __property__",
"changeCount" : "Ändern: __count__rules",
"regex" : "Reguläre Ausdrücke verwenden"
},
"action" : {
"set" : "Setze",
"change" : "Ändern",
"delete" : "Löschen",
"move" : "Bewegen",
"to" : "auf",
"search" : "Suchen nach",
"replace" : "Ersetzen durch"
},
"errors" : {
"invalid-from" : "Ungültiges 'from' Merkmal: __error__",
"invalid-json" : "Ungültiges 'to' JSON Merkmal",
"invalid-expr" : "Ungültiger JSONata Ausdruck: __error__"
}
},
"range" : {
"range" : "Bereich",
"label" : {
"action" : "Aktion",
"inputrange" : "von einem Eingabebereich",
"resultrange" : "in einen Zielbereich",
"from" : "von",
"to" : "auf",
"roundresult" : "Runde das Ergebnis auf die nächste ganze Zahl?"
},
"placeholder" : {
"min" : "z. B. 0",
"maxin" : "z. B. 99",
"maxout" : "z. B. 255"
},
"scale" : {
"payload" : "Skaliere die Nachrichteneigenschaft",
"limit" : "Skalieren und Begrenzen auf den Zielbereich",
"wrap" : "Skaliere und Einhüllen innerhalb des Zielbereichs"
},
"tip" : "Tipp: Dieser Node funktioniert NUR mit Zahlen.",
"errors" : {
"notnumber" : "Keine Zahl"
}
},
"csv" : {
"label" : {
"columns" : "Spalten",
"separator" : "Trennzeichen",
"c2o" : "Optionen für CSV-zu-Objekt",
"o2c" : "Objekt-zu-CSV-Optionen",
"input" : "Eingabe",
"skip-s" : "Zuerst überspringen",
"skip-e" : "Zeilen",
"firstrow" : "erste Zeile enthält Spaltennamen",
"output" : "Ausgabe",
"includerow" : "Spaltennamenszeile einschließen",
"newline" : "Zeilenneuerzeile"
},
"placeholder" : {
"columns" : "durch Kommas getrennte Spaltennamen"
},
"separator" : {
"comma" : "Komma",
"tab" : "Tabulatorzunge",
"space" : "Leerzeichen",
"semicolon" : "Semikolon",
"colon" : "Doppelpunkt",
"hashtag" : "hashtag",
"other" : "andere ..."
},
"output" : {
"row" : "eine Nachricht pro Zeile",
"array" : "eine einzelne Nachricht [ Array]"
},
"newline" : {
"linux" : "Linux (\\n)",
"mac" : "Mac (\\r)",
"windows" : "Windows (\\r \\n)"
},
"errors" : {
"csv_js" : "Dieser Node verarbeitet nur CSV-Zeichenfolgen oder JS-Objekte.",
"obj_csv" : "Es wurde keine Spaltenschablone für Objekt-> CSV angegeben."
}
},
"html" : {
"label" : {
"select" : "Selektor",
"output" : "Ausgabe",
"in" : "in"
},
"output" : {
"html" : "den HTML-Inhalt der Elemente",
"text" : "nur der Textinhalt der Elemente",
"attr" : "ein Objekt mit allen Attributen der Elemente"
},
"format" : {
"single" : "als einzelne Nachricht mit einem Array",
"multi" : "als mehrere Nachrichten, eine für jedes Element"
}
},
"json" : {
"errors" : {
"dropped-object" : "Ignorierte Nicht-Objekt-Nutzdaten",
"dropped" : "Ignorierter nicht unterstützter Nutzdatentyp",
"dropped-error" : "Fehler beim Konvertieren der Nutzdaten",
"schema-error" : "JSON-Schema-Fehler",
"schema-error-compile" : "JSON-Schema-Fehler: Schema konnte nicht kompiliert werden"
},
"label" : {
"o2j" : "Objekt zu JSON-Optionen",
"pretty" : "JSON-Zeichenfolge formatieren",
"action" : "Aktion",
"property" : "Eigenschaft",
"actions" : {
"toggle" : "Konvertieren zwischen JSON-Zeichenfolge und Objekt",
"str" : "Immer in JSON-Zeichenfolge konvertieren",
"obj" : "Immer in JavaScript-Objekt konvertieren"
}
}
},
"yaml" : {
"errors" : {
"dropped-object" : "Ignorierte Nicht-Objekt-Nutzdaten",
"dropped" : "Ignorierter nicht unterstützter Nutzdatentyp",
"dropped-error" : "Fehler beim Konvertieren der Nutzdaten"
}
},
"xml" : {
"label" : {
"represent" : "Eigenschaftsname für XML-Tagattribute",
"prefix" : "Eigenschaftsname für Tagtextinhalt",
"advanced" : "Erweiterte Optionen",
"x2o" : "Optionen für XML zu Objekt"
},
"errors" : {
"xml_js" : "Dieser Node verarbeitet nur XML-Zeichenfolgen oder JS-Objekte."
}
},
"file" : {
"label" : {
"filename" : "Name der Datei",
"action" : "Aktion",
"addnewline" : "Neue Zeile (\\n) zu den einzelnen Nutzdaten hinzufügen?",
"createdir" : "Verzeichnis erstellen, wenn es nicht vorhanden ist?",
"outputas" : "Ausgabe",
"breakchunks" : "In Chunks aufbrechen",
"breaklines" : "In Linien aufbrechen",
"filelabel" : "Datei",
"sendError" : "Nachricht bei Fehler senden (traditioneller Modus)",
"deletelabel" : "__file__ löschen"
},
"action" : {
"append" : "an Datei anhängen",
"overwrite" : "Datei überschreiben",
"delete" : "Datei löschen"
},
"output" : {
"utf8" : "eine einzelne utf8-Zeichenfolge",
"buffer" : "ein einzelnes Bufferobjekt",
"lines" : "ein Nachricht pro Zeile",
"stream" : "ein Datenstrom von Buffers"
},
"status" : {
"wrotefile" : "in Datei geschrieben: __file__",
"deletedfile" : "gelöschte Datei: __file__",
"appendedfile" : "an Datei angefügt: __file__"
},
"errors" : {
"nofilename" : "Kein Dateiname angegeben",
"invaliddelete" : "Warnung: Ungültiges Löschen. Bitte verwenden Sie im Konfigurationsdialog eine bestimmte Löschoption.",
"deletefail" : "Fehler beim Löschen der Datei: __error__",
"writefail" : "Schreiben in Datei fehlgeschlagen: __error__",
"appendfail" : "Anhängen an Datei fehlgeschlagen: __error__",
"createfail" : "Fehler beim Erstellen der Datei: __error__"
},
"tip" : "Tipp: Der Dateiname muss ein absoluter Pfad sein. Andernfalls wird er relativ zum Arbeitsverzeichnis des Node-RED-Prozesses verwendet."
},
"split" : {
"split" : "aufteilen",
"intro" : "Trennen Sie <code> msg.payload </code> basierend auf dem Typ:",
"object" : "<b> Objekt </b>",
"objectSend" : "Eine Nachricht für jedes Schlüssel/Wert-Paar senden",
"strBuff" : "<b> Zeichenfolge </b> / <b> Buffer </b>",
"array" : "<b> Array </b>",
"splitUsing" : "Trennen mit",
"splitLength" : "Feste Länge von",
"stream" : "Behandeln als Strom von Nachrichten",
"addname" : "Schlüssel kopieren nach "
},
"join" : {
"join" : "Join",
"mode" : {
"mode" : "Modus",
"auto" : "Automatisch",
"merge" : "Sequenzen zusammenführen",
"reduce" : "Reihenfolge bestimmen",
"custom" : "Manuell"
},
"combine" : "Kombiniere alle",
"create" : "und erstelle",
"type" : {
"string" : "eine Zeichenfolge",
"array" : "ein Array",
"buffer" : "ein Buffer",
"object" : "ein Schlüssel/Wert-Objekt",
"merged" : "ein zusammengefasstes Objekt"
},
"using" : "mit dem Wert von",
"key" : "als Schlüssel",
"joinedUsing" : "verbunden mit",
"send" : "Senden Sie die Nachricht:",
"afterCount" : "nach einer Reihe von Nachrichtenteilen",
"count" : "Zähler",
"subsequent" : "und jede nachfolgende Nachricht.",
"afterTimeout" : "nach Zeitlimitüberschreitung von",
"seconds" : "Sekunden",
"complete" : "Nach einer Nachricht mit der gesetzten Eigenschaft <code>msg.complete</code>",
"tip" : "Dieser Modus setzt voraus, dass dieser Node entweder mit einem <i> split </i> Node verbunden ist oder dass die empfangenen Nachrichten über eine ordnungsgemäß konfigurierte Eigenschaft <code> msg.parts </code> verfügen.",
"too-many" : "Zu viele anstehende Nachrichten im Verknüpfungs-Node",
"merge" : {
"topics-label" : "Zusammengemiedene Themen",
"topics" : "Themen",
"topic" : "Topic",
"on-change" : "Zusammengefügte Nachricht bei Ankunft eines neuen Topics senden"
},
"reduce" : {
"exp" : "Zusammenfassen durch",
"exp-value" : "Ausdruck",
"init" : "Anfangswert",
"right" : "In umgekehrter Reihenfolge auswerten (letzter auf den ersten)",
"fixup" : "Fix-up"
},
"errors" : {
"invalid-expr" : "Ungültiger JSONata-Ausdruck: __error__"
=======
},
"inject": {
"inject": "inject",

View File

@ -22,6 +22,10 @@
<dd>Sets the delay, in milliseconds, to be applied to the message. This
option only applies if the node is configured to allow the message to
override the configured default delay interval.</dd>
<dt class="optional">rate <span class="property-type">number</span></dt>
<dd>Sets the rate value in milliseconds between messages.
This node overwrites the existing rate value defined in the node configuration
when it receives the message which contains <code>msg.rate</code> value.</dd>
<dt class="optional">reset</dt>
<dd>If the received message has this property set to any value, all
outstanding messages held by the node are cleared without being sent.</dd>

View File

@ -301,9 +301,8 @@
}
}
},
"error": {
"buffer": "buffer exceeded 1000 messages",
"buffer1": "buffer exceeded 10000 messages"
"errors": {
"too-many" : "too many pending messages in delay node"
}
},
"trigger": {

View File

@ -20,6 +20,8 @@
<dl class="message-properties">
<dt class="optional">delay <span class="property-type">数値</span></dt>
<dd>メッセージの遅延時間をミリ秒単位で設定します。これはノードの設定でデフォルトの遅延時間を上書きできるようノードを設定した場合にのみ適用します。</dd>
<dt class="optional">rate <span class="property-type">数値</span></dt>
<dd>メッセージ間のレート値をミリ秒単位で設定します。本ノードは、<code>msg.rate</code>値を含むメッセージを受け取ると、ノード設定で定義された既存のレート値を上書きします。</dd>
<dt class="optional">reset</dt>
<dd>受信メッセージでこのプロパティを任意の値に設定すると、ノードが保持する全ての未送信メッセージをクリアします。</dd>
<dt class="optional">flush</dt>

View File

@ -301,9 +301,8 @@
}
}
},
"error": {
"buffer": "バッファ上限の1000メッセージを超えました",
"buffer1": "バッファ上限の10000メッセージを超えました"
"errors": {
"too-many": "delayード内で保持しているメッセージが多すぎます"
}
},
"trigger": {

View File

@ -272,10 +272,6 @@
"singular": "일"
}
}
},
"error": {
"buffer": "버퍼 상한의 1000메세지를 넘었습니다",
"buffer1": "버퍼 상한의 10000메세지를 넘었습니다"
}
},
"trigger": {

View File

@ -290,10 +290,6 @@
"singular": "天"
}
}
},
"error": {
"buffer": "缓冲了超过 1000 条信息",
"buffer1": "缓冲了超过 10000 条信息"
}
},
"trigger": {

View File

@ -18,6 +18,7 @@ var should = require("should");
var delayNode = require("nr-test-utils").require("@node-red/nodes/core/function/89-delay.js");
var helper = require("node-red-node-test-helper");
var RED = require("nr-test-utils").require("node-red/lib/red");
var GRACE_PERCENTAGE=10;
@ -35,6 +36,7 @@ describe('delay Node', function() {
});
afterEach(function(done) {
RED.settings.nodeMessageBufferMaxLength = 0;
helper.unload();
helper.stopServer(done);
});
@ -150,6 +152,7 @@ describe('delay Node', function() {
* We send a message, take a timestamp then when the message is received by the helper node, we take another timestamp.
* Then check if the message has been delayed by the expected amount.
*/
it('delays the message in seconds', function(done) {
genericDelayTest(0.5, "seconds", done);
});
@ -176,14 +179,14 @@ describe('delay Node', function() {
* @param nbUnit - the multiple of the unit, aLimit Message for nbUnit Seconds
* @param runtimeInMillis - when to terminate run and count messages received
*/
function genericRateLimitSECONDSTest(aLimit, nbUnit, runtimeInMillis, done) {
function genericRateLimitSECONDSTest(aLimit, nbUnit, runtimeInMillis, rateValue, done) {
var flow = [{"id":"delayNode1","type":"delay","nbRateUnits":nbUnit,"name":"delayNode","pauseType":"rate","timeout":5,"timeoutUnits":"seconds","rate":aLimit,"rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"wires":[["helperNode1"]]},
{id:"helperNode1", type:"helper", wires:[]}];
helper.load(delayNode, flow, function() {
var delayNode1 = helper.getNode("delayNode1");
var helperNode1 = helper.getNode("helperNode1");
var receivedMessagesStack = [];
var rate = 1000/aLimit;
var rate = 1000 / aLimit * nbUnit;
var receiveTimestamp;
@ -201,7 +204,7 @@ describe('delay Node', function() {
var i = 0;
for (; i < possibleMaxMessageCount + 1; i++) {
delayNode1.receive({payload:i});
delayNode1.receive({ payload: i, rate: rateValue });
}
setTimeout(function() {
@ -224,17 +227,22 @@ describe('delay Node', function() {
}
it('limits the message rate to 1 per second', function(done) {
genericRateLimitSECONDSTest(1, 1, 1500, done);
genericRateLimitSECONDSTest(1, 1, 1500, null, done);
});
it('limits the message rate to 1 per 2 seconds', function(done) {
this.timeout(6000);
genericRateLimitSECONDSTest(1, 2, 3000, done);
genericRateLimitSECONDSTest(1, 2, 3000, null, done);
});
it('limits the message rate to 2 per seconds, 2 seconds', function(done) {
this.timeout(6000);
genericRateLimitSECONDSTest(2, 1, 2100, done);
genericRateLimitSECONDSTest(2, 1, 2100, null, done);
});
it('limits the message rate using msg.rate', function (done) {
RED.settings.nodeMessageBufferMaxLength = 3;
genericRateLimitSECONDSTest(1, 1, 1500, 2000, done);
});
/**
@ -243,7 +251,7 @@ describe('delay Node', function() {
* @param nbUnit - the multiple of the unit, aLimit Message for nbUnit Seconds
* @param runtimeInMillis - when to terminate run and count messages received
*/
function dropRateLimitSECONDSTest(aLimit, nbUnit, runtimeInMillis, done) {
function dropRateLimitSECONDSTest(aLimit, nbUnit, runtimeInMillis, rateValue, done) {
var flow = [{"id":"delayNode1","type":"delay","name":"delayNode","pauseType":"rate","timeout":5,"nbRateUnits":nbUnit,"timeoutUnits":"seconds","rate":aLimit,"rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":true,"wires":[["helperNode1"]]},
{id:"helperNode1", type:"helper", wires:[]}];
helper.load(delayNode, flow, function() {
@ -269,7 +277,7 @@ describe('delay Node', function() {
var possibleMaxMessageCount = Math.ceil(aLimit * (runtimeInMillis / 1000) + aLimit); // +aLimit as at the start of the 2nd period, we're allowing the 3rd burst
var i = 0;
delayNode1.receive({payload:i});
delayNode1.receive({ payload: i, rate: rateValue });
i++;
for (; i < possibleMaxMessageCount + 1; i++) {
setTimeout(function() {
@ -306,17 +314,23 @@ describe('delay Node', function() {
it('limits the message rate to 1 per second, 4 seconds, with drop', function(done) {
this.timeout(6000);
dropRateLimitSECONDSTest(1, 1, 4000, done);
dropRateLimitSECONDSTest(1, 1, 4000, null, done);
});
it('limits the message rate to 1 per 2 seconds, 4 seconds, with drop', function(done) {
this.timeout(6000);
dropRateLimitSECONDSTest(1, 2, 4500, done);
dropRateLimitSECONDSTest(1, 2, 4500, null, done);
});
it('limits the message rate to 2 per second, 5 seconds, with drop', function(done) {
this.timeout(6000);
dropRateLimitSECONDSTest(2, 1, 5000, done);
dropRateLimitSECONDSTest(2, 1, 5000, null, done);
});
it('limits the message rate with drop using msg.rate', function (done) {
this.timeout(6000);
RED.settings.nodeMessageBufferMaxLength = 3;
dropRateLimitSECONDSTest(2, 1, 5000, 1000, done);
});
/**
@ -719,7 +733,7 @@ describe('delay Node', function() {
setImmediate( function() { delayNode1.receive({reset:true}); }); // reset the queue
});
});
/* Messaging API support */
function mapiDoneTestHelper(done, pauseType, drop, msgAndTimings) {
const completeNode = require("nr-test-utils").require("@node-red/nodes/core/common/24-complete.js");
@ -747,7 +761,7 @@ describe('delay Node', function() {
}
});
}
it('calls done when queued message is emitted (type: delay)', function(done) {
mapiDoneTestHelper(done, "delay", false, [{msg:{payload:1}, avr:1000, var:100}]);
});