Merge branch 'master' into 4843-quick-add-placement

This commit is contained in:
Stephen McLaughlin 2024-08-04 11:38:04 +01:00 committed by GitHub
commit b6a25518e3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 69 additions and 27 deletions

View File

@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:
node-version: [18, 20, 22] node-version: [18, 20, 22.4.x]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }} - name: Use Node.js ${{ matrix.node-version }}

View File

@ -32,24 +32,28 @@ RED.contextMenu = (function () {
const canRemoveFromGroup = hasSelection && !!selection.nodes[0].g const canRemoveFromGroup = hasSelection && !!selection.nodes[0].g
let hasGroup, isAllGroups = true, hasDisabledNode, hasEnabledNode, hasLabeledNode, hasUnlabeledNode; let hasGroup, isAllGroups = true, hasDisabledNode, hasEnabledNode, hasLabeledNode, hasUnlabeledNode;
if (hasSelection) { if (hasSelection) {
selection.nodes.forEach(n => { const nodes = selection.nodes.slice();
while (nodes.length) {
const n = nodes.shift();
if (n.type === 'group') { if (n.type === 'group') {
hasGroup = true; hasGroup = true;
nodes.push(...n.nodes);
} else { } else {
isAllGroups = false; isAllGroups = false;
} if (n.d) {
if (n.d) { hasDisabledNode = true;
hasDisabledNode = true; } else {
} else { hasEnabledNode = true;
hasEnabledNode = true; }
} }
if (n.l === undefined || n.l) { if (n.l === undefined || n.l) {
hasLabeledNode = true; hasLabeledNode = true;
} else { } else {
hasUnlabeledNode = true; hasUnlabeledNode = true;
} }
}); }
} }
const offset = $("#red-ui-workspace-chart").offset() const offset = $("#red-ui-workspace-chart").offset()
let addX = options.x - offset.left + $("#red-ui-workspace-chart").scrollLeft() let addX = options.x - offset.left + $("#red-ui-workspace-chart").scrollLeft()

View File

@ -21,7 +21,7 @@
<input type="text" id="node-input-name" data-i18n="[placeholder]node-red:common.label.name"> <input type="text" id="node-input-name" data-i18n="[placeholder]node-red:common.label.name">
</div> </div>
<div class="form-row"> <div class="form-row">
<label for="node-input-property"><i class="fa fa-forward"></i> <span data-i18n="split.split"></span></label> <label for="node-input-property"><i class="fa fa-forward"></i> <span data-i18n="split.splitThe"></span></label>
<input type="text" id="node-input-property" style="width:70%;"/> <input type="text" id="node-input-property" style="width:70%;"/>
</div> </div>
<div class="form-row"><span data-i18n="[html]split.strBuff"></span></div> <div class="form-row"><span data-i18n="[html]split.strBuff"></span></div>

View File

@ -36,6 +36,10 @@
<label style="margin-left: 10px; width: 175px;" for="node-input-overlap" data-i18n="batch.count.overlap"></label> <label style="margin-left: 10px; width: 175px;" for="node-input-overlap" data-i18n="batch.count.overlap"></label>
<input type="text" id="node-input-overlap" style="width: 50px;"> <input type="text" id="node-input-overlap" style="width: 50px;">
</div> </div>
<div class="form-row">
<input type="checkbox" id="node-input-honourParts" style="margin-left: 10px; margin-right:10px; vertical-align:top; width:auto;">
<label for="node-input-honourParts" style="width:auto;" data-i18n="batch.honourParts"></label>
</div>
</div> </div>
<div class="node-row-msg-interval"> <div class="node-row-msg-interval">
@ -45,7 +49,7 @@
<span data-i18n="batch.interval.seconds"></span> <span data-i18n="batch.interval.seconds"></span>
</div> </div>
<div class="form-row"> <div class="form-row">
<input type="checkbox" id="node-input-allowEmptySequence" style="margin-left:20px; margin-right: 10px; vertical-align:top; width:auto;"> <input type="checkbox" id="node-input-allowEmptySequence" style="margin-left:20px; margin-right:10px; vertical-align:top; width:auto;">
<label for="node-input-allowEmptySequence" style="width:auto;" data-i18n="batch.interval.empty"></label> <label for="node-input-allowEmptySequence" style="width:auto;" data-i18n="batch.interval.empty"></label>
</div> </div>
</div> </div>
@ -101,6 +105,7 @@
} }
}, },
allowEmptySequence: {value:false}, allowEmptySequence: {value:false},
honourParts: {value:false},
topics: {value:[{topic:""}]} topics: {value:[{topic:""}]}
}, },
inputs:1, inputs:1,

View File

@ -181,6 +181,8 @@ module.exports = function(RED) {
RED.nodes.createNode(this,n); RED.nodes.createNode(this,n);
var node = this; var node = this;
var mode = n.mode || "count"; var mode = n.mode || "count";
var eof = false;
node.honourParts = n.honourParts || false;
node.pending_count = 0; node.pending_count = 0;
if (mode === "count") { if (mode === "count") {
@ -201,9 +203,12 @@ module.exports = function(RED) {
return; return;
} }
var queue = node.pending; var queue = node.pending;
if (node.honourParts && msg.hasOwnProperty("parts")) {
if (msg.parts.index + 1 === msg.parts.count) { eof = true; }
}
queue.push({msg, send, done}); queue.push({msg, send, done});
node.pending_count++; node.pending_count++;
if (queue.length === count) { if (queue.length === count || eof === true) {
send_msgs(node, queue, is_overlap); send_msgs(node, queue, is_overlap);
for (let i = 0; i < queue.length-overlap; i++) { for (let i = 0; i < queue.length-overlap; i++) {
queue[i].done(); queue[i].done();
@ -211,6 +216,7 @@ module.exports = function(RED) {
node.pending = node.pending =
(overlap === 0) ? [] : queue.slice(-overlap); (overlap === 0) ? [] : queue.slice(-overlap);
node.pending_count = 0; node.pending_count = 0;
eof = false;
} }
var max_msgs = max_kept_msgs_count(node); var max_msgs = max_kept_msgs_count(node);
if ((max_msgs > 0) && (node.pending_count > max_msgs)) { if ((max_msgs > 0) && (node.pending_count > max_msgs)) {

View File

@ -912,6 +912,7 @@
"objectSend": "Sende eine Nachricht für jedes Schlüssel/Wert-Paar", "objectSend": "Sende eine Nachricht für jedes Schlüssel/Wert-Paar",
"strBuff": "<b>string</b> / <b>buffer</b>", "strBuff": "<b>string</b> / <b>buffer</b>",
"array": "<b>array</b>", "array": "<b>array</b>",
"splitThe": "Split",
"splitUsing": "Aufteilung", "splitUsing": "Aufteilung",
"splitLength": "feste Längen von", "splitLength": "feste Längen von",
"stream": "Als Nachrichtenstrom behandeln (Streaming-Modus)", "stream": "Als Nachrichtenstrom behandeln (Streaming-Modus)",

View File

@ -1011,12 +1011,13 @@
"tip": "Tip: The filename should be an absolute path, otherwise it will be relative to the working directory of the Node-RED process." "tip": "Tip: The filename should be an absolute path, otherwise it will be relative to the working directory of the Node-RED process."
}, },
"split": { "split": {
"split": "Split", "split": "split",
"intro": "Split <code>msg.payload</code> based on type:", "intro": "Split <code>msg.payload</code> based on type:",
"object": "<b>Object</b>", "object": "<b>Object</b>",
"objectSend": "Send a message for each key/value pair", "objectSend": "Send a message for each key/value pair",
"strBuff": "<b>String</b> / <b>Buffer</b>", "strBuff": "<b>String</b> / <b>Buffer</b>",
"array": "<b>Array</b>", "array": "<b>Array</b>",
"splitThe": "Split the",
"splitUsing": "Split using", "splitUsing": "Split using",
"splitLength": "Fixed length of", "splitLength": "Fixed length of",
"stream": "Handle as a stream of messages", "stream": "Handle as a stream of messages",
@ -1113,6 +1114,7 @@
"too-many": "too many pending messages in batch node", "too-many": "too many pending messages in batch node",
"unexpected": "unexpected mode", "unexpected": "unexpected mode",
"no-parts": "no parts property in message", "no-parts": "no parts property in message",
"honourParts": "Allow msg.parts to also complete batch operation.",
"error": { "error": {
"invalid-count": "Invalid count", "invalid-count": "Invalid count",
"invalid-overlap": "Invalid overlap", "invalid-overlap": "Invalid overlap",

View File

@ -1017,6 +1017,7 @@
"objectSend": "各key/valueペアのメッセージを送信", "objectSend": "各key/valueペアのメッセージを送信",
"strBuff": "<b>文字列</b> / <b>バッファ</b>", "strBuff": "<b>文字列</b> / <b>バッファ</b>",
"array": "<b>配列</b>", "array": "<b>配列</b>",
"splitThe": "に基づく",
"splitUsing": "分割", "splitUsing": "分割",
"splitLength": "固定長", "splitLength": "固定長",
"stream": "メッセージのストリームとして処理", "stream": "メッセージのストリームとして処理",

View File

@ -994,6 +994,7 @@
"objectSend": "Envia uma mensagem para cada par chave/valor", "objectSend": "Envia uma mensagem para cada par chave/valor",
"strBuff": "<b>Cadeia de caracteres</b> / <b>Armazenamento Temporário</b>", "strBuff": "<b>Cadeia de caracteres</b> / <b>Armazenamento Temporário</b>",
"array": "<b>Matriz</b>", "array": "<b>Matriz</b>",
"splitThe": "Dividir",
"splitUsing": "Dividir usando", "splitUsing": "Dividir usando",
"splitLength": "Comprimento fixo de", "splitLength": "Comprimento fixo de",
"stream": "Tratar como uma transmissão de mensagens", "stream": "Tratar como uma transmissão de mensagens",

View File

@ -874,6 +874,7 @@
"objectSend":"Отправлять сообщение для каждой пары ключ/значение", "objectSend":"Отправлять сообщение для каждой пары ключ/значение",
"strBuff":"<b>Строка</b> / <b>Буфер</b>", "strBuff":"<b>Строка</b> / <b>Буфер</b>",
"array":"<b>Массив</b>", "array":"<b>Массив</b>",
"splitThe": "Pазделить",
"splitUsing":"С помощью", "splitUsing":"С помощью",
"splitLength":"Фикс. длина", "splitLength":"Фикс. длина",
"stream":"Обрабатывать как поток сообщений", "stream":"Обрабатывать как поток сообщений",

View File

@ -997,6 +997,7 @@
"objectSend": "每个键值对作为单个消息发送", "objectSend": "每个键值对作为单个消息发送",
"strBuff": "<b>字符串</b> / <b>Buffer</b>", "strBuff": "<b>字符串</b> / <b>Buffer</b>",
"array": "<b>数组</b>", "array": "<b>数组</b>",
"splitThe": "Split",
"splitUsing": "拆分使用", "splitUsing": "拆分使用",
"splitLength": "固定长度", "splitLength": "固定长度",
"stream": "作为消息流处理", "stream": "作为消息流处理",

View File

@ -866,6 +866,7 @@
"objectSend": "每個鍵值對作為單個消息發送", "objectSend": "每個鍵值對作為單個消息發送",
"strBuff": "<b>字串</b> / <b>Buffer</b>", "strBuff": "<b>字串</b> / <b>Buffer</b>",
"array": "<b>陣列</b>", "array": "<b>陣列</b>",
"splitThe": "Split",
"splitUsing": "拆分使用", "splitUsing": "拆分使用",
"splitLength": "固定長度", "splitLength": "固定長度",
"stream": "作為消息流處理", "stream": "作為消息流處理",

View File

@ -98,7 +98,7 @@ describe('BATCH node', function() {
var n2 = helper.getNode("n2"); var n2 = helper.getNode("n2");
check_data(n1, n2, results, done); check_data(n1, n2, results, done);
for(var i = 0; i < 6; i++) { for(var i = 0; i < 6; i++) {
n1.receive({payload: i}); n1.receive({payload: i, parts: { count:6, index:i }});
} }
}); });
} }
@ -168,6 +168,25 @@ describe('BATCH node', function() {
check_count(flow, results, done); check_count(flow, results, done);
}); });
it('should create seq. with count (more sent than count)', function(done) {
var flow = [{id:"n1", type:"batch", name: "BatchNode", mode: "count", count: 4, overlap: 0, interval: 10, allowEmptySequence: false, topics: [], wires:[["n2"]]},
{id:"n2", type:"helper"}];
var results = [
[0, 1, 2, 3]
];
check_count(flow, results, done);
});
it('should create seq. with count and terminate early if parts honoured', function(done) {
var flow = [{id:"n1", type:"batch", name: "BatchNode", mode: "count", count: 4, overlap: 0, interval: 10, allowEmptySequence:false, honourParts:true, topics: [], wires:[["n2"]]},
{id:"n2", type:"helper"}];
var results = [
[0, 1, 2, 3],
[4, 5]
];
check_count(flow, results, done);
});
it('should create seq. with count and overlap', function(done) { it('should create seq. with count and overlap', function(done) {
var flow = [{id:"n1", type:"batch", name: "BatchNode", mode: "count", count: 3, overlap: 2, interval: 10, allowEmptySequence: false, topics: [], wires:[["n2"]]}, var flow = [{id:"n1", type:"batch", name: "BatchNode", mode: "count", count: 3, overlap: 2, interval: 10, allowEmptySequence: false, topics: [], wires:[["n2"]]},
{id:"n2", type:"helper"}]; {id:"n2", type:"helper"}];