From 05c924a9df670843654f43787b1a5af36460b3f2 Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Thu, 4 Jul 2024 15:09:55 +0100 Subject: [PATCH] Let batch node terminate "early" if msg.parts set to end of sequence (ie index = count -1). Useful to close at end of files etc. --- .../nodes/core/sequence/19-batch.html | 7 ++++- .../@node-red/nodes/core/sequence/19-batch.js | 8 +++++- .../nodes/locales/en-US/messages.json | 1 + test/nodes/core/sequence/19-batch_spec.js | 27 ++++++++++++++++--- 4 files changed, 37 insertions(+), 6 deletions(-) diff --git a/packages/node_modules/@node-red/nodes/core/sequence/19-batch.html b/packages/node_modules/@node-red/nodes/core/sequence/19-batch.html index 9afb84205..91e6567c5 100644 --- a/packages/node_modules/@node-red/nodes/core/sequence/19-batch.html +++ b/packages/node_modules/@node-red/nodes/core/sequence/19-batch.html @@ -36,6 +36,10 @@ +
+ + +
@@ -45,7 +49,7 @@
- +
@@ -101,6 +105,7 @@ } }, allowEmptySequence: {value:false}, + honourParts: {value:false}, topics: {value:[{topic:""}]} }, inputs:1, diff --git a/packages/node_modules/@node-red/nodes/core/sequence/19-batch.js b/packages/node_modules/@node-red/nodes/core/sequence/19-batch.js index f3f29df6a..5cc97e6a9 100644 --- a/packages/node_modules/@node-red/nodes/core/sequence/19-batch.js +++ b/packages/node_modules/@node-red/nodes/core/sequence/19-batch.js @@ -181,6 +181,8 @@ module.exports = function(RED) { RED.nodes.createNode(this,n); var node = this; var mode = n.mode || "count"; + var eof = false; + node.honourParts = n.honourParts || false; node.pending_count = 0; if (mode === "count") { @@ -201,9 +203,12 @@ module.exports = function(RED) { return; } 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}); node.pending_count++; - if (queue.length === count) { + if (queue.length === count || eof === true) { send_msgs(node, queue, is_overlap); for (let i = 0; i < queue.length-overlap; i++) { queue[i].done(); @@ -211,6 +216,7 @@ module.exports = function(RED) { node.pending = (overlap === 0) ? [] : queue.slice(-overlap); node.pending_count = 0; + eof = false; } var max_msgs = max_kept_msgs_count(node); if ((max_msgs > 0) && (node.pending_count > max_msgs)) { diff --git a/packages/node_modules/@node-red/nodes/locales/en-US/messages.json b/packages/node_modules/@node-red/nodes/locales/en-US/messages.json index 560d192c1..37a249567 100644 --- a/packages/node_modules/@node-red/nodes/locales/en-US/messages.json +++ b/packages/node_modules/@node-red/nodes/locales/en-US/messages.json @@ -1112,6 +1112,7 @@ "too-many": "too many pending messages in batch node", "unexpected": "unexpected mode", "no-parts": "no parts property in message", + "honourParts": "Allow msg.parts to also complete batch operation.", "error": { "invalid-count": "Invalid count", "invalid-overlap": "Invalid overlap", diff --git a/test/nodes/core/sequence/19-batch_spec.js b/test/nodes/core/sequence/19-batch_spec.js index 2ebcb8d4d..b4025a3f8 100644 --- a/test/nodes/core/sequence/19-batch_spec.js +++ b/test/nodes/core/sequence/19-batch_spec.js @@ -98,7 +98,7 @@ describe('BATCH node', function() { var n2 = helper.getNode("n2"); check_data(n1, n2, results, done); 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); }); + 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) { 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"}]; @@ -455,7 +474,7 @@ describe('BATCH node', function() { function mapiDoneTestHelper(done, mode, count, overlap, interval, allowEmptySequence, msgAndTimings) { const completeNode = require("nr-test-utils").require("@node-red/nodes/core/common/24-complete.js"); const catchNode = require("nr-test-utils").require("@node-red/nodes/core/common/25-catch.js"); - const flow = [{id:"batchNode1", type:"batch", name: "BatchNode", mode, count, overlap, interval, + const flow = [{id:"batchNode1", type:"batch", name: "BatchNode", mode, count, overlap, interval, allowEmptySequence, topics: [{topic: "TA"}], wires:[[]]}, {id:"completeNode1",type:"complete",scope: ["batchNode1"],uncaught:false,wires:[["helperNode1"]]}, {id:"catchNode1", type:"catch",scope: ["batchNode1"],uncaught:false,wires:[["helperNode1"]]}, @@ -482,13 +501,13 @@ describe('BATCH node', function() { } it('should call done() when message is sent (mode: count)', function(done) { - mapiDoneTestHelper(done, "count", 2, 0, 2, false, [ + mapiDoneTestHelper(done, "count", 2, 0, 2, false, [ { msg: {payload: 0}, delay: 0, avr: 0, var: 100}, { msg: {payload: 1}, delay: 0, avr: 0, var: 100} ]); }); it('should call done() when reset (mode: count)', function(done) { - mapiDoneTestHelper(done, "count", 2, 0, 2, false, [ + mapiDoneTestHelper(done, "count", 2, 0, 2, false, [ { msg: {payload: 0}, delay: 0, avr: 200, var: 100}, { msg: {payload: 1, reset:true}, delay: 200, avr: 200, var: 100} ]);