mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
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:
parent
719aea2a58
commit
a20049c82a
@ -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();
|
||||
}
|
||||
|
@ -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; oder wss: / & #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",
|
||||
|
@ -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>
|
||||
|
@ -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": {
|
||||
|
@ -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>
|
||||
|
@ -301,9 +301,8 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"buffer": "バッファ上限の1000メッセージを超えました",
|
||||
"buffer1": "バッファ上限の10000メッセージを超えました"
|
||||
"errors": {
|
||||
"too-many": "delayノード内で保持しているメッセージが多すぎます"
|
||||
}
|
||||
},
|
||||
"trigger": {
|
||||
|
@ -272,10 +272,6 @@
|
||||
"singular": "일"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"buffer": "버퍼 상한의 1000메세지를 넘었습니다",
|
||||
"buffer1": "버퍼 상한의 10000메세지를 넘었습니다"
|
||||
}
|
||||
},
|
||||
"trigger": {
|
||||
|
@ -290,10 +290,6 @@
|
||||
"singular": "天"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"buffer": "缓冲了超过 1000 条信息",
|
||||
"buffer1": "缓冲了超过 10000 条信息"
|
||||
}
|
||||
},
|
||||
"trigger": {
|
||||
|
@ -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);
|
||||
});
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user