Add buffer joiner mode to Join node

This commit is contained in:
Nick O'Leary 2017-06-27 17:10:52 +01:00
parent d99432bff1
commit 52c0d360b2
No known key found for this signature in database
GPG Key ID: 4F2157149161A6C9
4 changed files with 103 additions and 39 deletions

View File

@ -812,10 +812,11 @@
"type":{
"string":"a String",
"array":"an Array",
"buffer":"a Buffer",
"object":"a key/value Object",
"merged":"a merged Object"
},
"using":"using",
"using":"using the value of",
"key":"as the key",
"joinedUsing":"joined using",
"send":"Send the message:",

View File

@ -181,21 +181,23 @@
<div class="form-row">
<label data-i18n="join.create"></label>
<select id="node-input-build" style="width:70%;">
<option id="node-input-build-string" value="string" data-i18n="join.type.string"></option>
<option value="string" data-i18n="join.type.string"></option>
<option value="buffer" data-i18n="join.type.buffer"></option>
<option value="array" data-i18n="join.type.array"></option>
<option value="object" data-i18n="join.type.object"></option>
<option value="merged" data-i18n="join.type.merged"></option>
</select>
</div>
<div class="form-row node-row-key">
<label style="vertical-align:top; margin-top:7px;" data-i18n="join.using"></label>
<label style="vertical-align:top; margin-top:7px; width:auto; margin-right: 5px;" data-i18n="join.using"></label>
<div style="display:inline-block">
<input type="text" id="node-input-key" style="width:252px;"> <span data-i18n="join.key"></span>
<input type="text" id="node-input-key" style="width:220px;"> <span data-i18n="join.key"></span>
</div>
</div>
<div class="form-row node-row-joiner">
<label for="node-input-joiner" data-i18n="join.joinedUsing"></label>
<input type="text" id="node-input-joiner" style="width:40px">
<input type="text" id="node-input-joiner" style="width:70%">
<input type="hidden" id="node-input-joinerType">
</div>
<div class="form-row node-row-trigger" id="trigger-row">
<label style="width:auto;" data-i18n="join.send"></label>
@ -203,8 +205,8 @@
<li>
<label style="width:280px;" for="node-input-count" data-i18n="join.afterCount"></label> <input id="node-input-count" data-i18n="[placeholder]join.count" type="text" style="width:75px;">
</li>
<li style="list-style-type:none;">
<input type="checkbox" id="node-input-accumulate" style="display:inline-block; width:20px; list-style-type:none; margin-left:20px; vertical-align:top;"> <span data-i18n="join.subsequent"></span>
<li class="node-row-accumulate" style="list-style-type:none;">
<input type="checkbox" id="node-input-accumulate" style="display:inline-block; width:20px; margin-left:20px; vertical-align:top;"> <label style="width: auto" for="node-input-accumulate" data-i18n="join.subsequent"></label>
</li>
<li>
<label style="width:280px;" for="node-input-timeout" data-i18n="join.afterTimeout"></label> <input id="node-input-timeout" data-i18n="[placeholder]join.seconds" type="text" style="width:75px;">
@ -249,14 +251,16 @@
<p>When configured to join in manual mode, the node is able to join sequences
of messages in a variety of ways.</p>
<ul>
<li>a <b>string</b> - created by joining the selected property of each message with the specified join character.</li>
<li>an <b>array</b>.</li>
<li>a <b>string</b> or <b>buffer</b> - created by joining the selected property of each message with the specified join characters or buffer.</li>
<li>an <b>array</b> - created by adding each selected property, or entire message, to the output array.</li>
<li>a <b>key/value object</b> - created by using a property of each message to determine the key under which
the required value is stored.</li>
<li>a <b>merged object</b> - created by merging the property of each message under a single object.</li>
</ul>
<p>The other properties of the output message are taken from the last message received before the result is sent.</p>
<p>A <i>count</i> can be set for how many messages should be received before generating the output message.</p>
<p>A <i>count</i> can be set for how many messages should be received before generating the output message.
For object outputs, once this count has been reached, the node can be configured to send a message for each subsequent message
received.</p>
<p>A <i>timeout</i> can be set to trigger sending the new message using whatever has been received so far.</p>
<p>If a message is received with the <b>msg.complete</b> property set, the output message is sent.</p>
</script>
@ -273,8 +277,8 @@
propertyType: { value:"msg"},
key: {value:"topic"},
joiner: { value:"\\n"},
joinerType: { value:"str"},
accumulate: { value:"false" },
//topic: { value:""},
timeout: {value:""},
count: {value:""}
},
@ -300,15 +304,25 @@
$("#node-input-build").change(function(e) {
var val = $(this).val();
$(".node-row-key").toggle(val==='object');
$(".node-row-joiner").toggle(val==='string');
$(".node-row-accumulate").toggle(val==='object' || val==='merged');
$(".node-row-joiner").toggle(val==='string' || val==='buffer');
$(".node-row-trigger").toggle(val!=='auto');
if (val === 'string') {
if (val === 'string' || val==='buffer') {
$("#node-input-property").typedInput('types',['msg']);
} else {
$("#node-input-property").typedInput('types',['msg', {value:"full",label:"complete message",hasValue:false}]);
}
});
$("#node-input-joiner").typedInput({
default: 'str',
typeField: $("#node-input-joinerType"),
types:[
'str',
'bin'
]
});
$("#node-input-property").typedInput({
typeField: $("#node-input-propertyType"),
types:['msg', {value:"full", label:"complete message", hasValue:false}]
@ -320,6 +334,12 @@
$("#node-input-build").change();
$("#node-input-mode").change();
},
oneditsave: function() {
var build = $("#node-input-build").val();
if (build !== 'object' && build !== 'merged') {
$("#node-input-accumulate").prop("checked",false);
}
}
});
</script>

View File

@ -66,7 +66,7 @@ module.exports = function(RED) {
return;
}
node.c = 0;
node.buffer = new Buffer.from([]);
node.buffer = Buffer.from([]);
this.on("input", function(msg) {
if (msg.hasOwnProperty("payload")) {
if (msg.hasOwnProperty("parts")) { msg.parts = { parts:msg.parts }; } // push existing parts to a stack
@ -182,7 +182,7 @@ module.exports = function(RED) {
msg.payload = node.buffer;
msg.parts.index = node.c++;
node.send(RED.util.cloneMessage(msg));
node.buffer = new Buffer.from([]);
node.buffer = Buffer.from([]);
}
}
else {
@ -243,7 +243,20 @@ module.exports = function(RED) {
this.key = n.key||"topic";
this.timer = (this.mode === "auto") ? 0 : Number(n.timeout || 0)*1000;
this.count = Number(n.count || 0);
this.joiner = (n.joiner||"").replace(/\\n/g,"\n").replace(/\\r/g,"\r").replace(/\\t/g,"\t").replace(/\\e/g,"\e").replace(/\\f/g,"\f").replace(/\\0/g,"\0");
this.joiner = n.joiner||"";
this.joinerType = n.joinerType||"str";
if (this.joinerType === "str") {
this.joiner = this.joiner.replace(/\\n/g,"\n").replace(/\\r/g,"\r").replace(/\\t/g,"\t").replace(/\\e/g,"\e").replace(/\\f/g,"\f").replace(/\\0/g,"\0");
} else if (this.joinerType === "bin") {
var joinArray = JSON.parse(n.joiner)
if (Array.isArray(joinArray)) {
this.joiner = Buffer.from(joinArray);
} else {
throw new Error("not an array");
}
}
this.build = n.build || "array";
this.accumulate = n.accumulate || "false";
//this.topic = n.topic;
@ -281,7 +294,11 @@ module.exports = function(RED) {
}
if (group.type === 'string') {
RED.util.setMessageProperty(group.msg,node.property,group.payload.join(group.joinChar));
var groupJoinChar = group.joinChar;
if (typeof group.joinChar !== 'string') {
groupJoinChar = group.joinChar.toString();
}
RED.util.setMessageProperty(group.msg,node.property,group.payload.join(groupJoinChar));
} else {
RED.util.setMessageProperty(group.msg,node.property,group.payload);
}
@ -378,7 +395,7 @@ module.exports = function(RED) {
type:payloadType,
msg:msg
}
if (payloadType === 'string') {
if (payloadType === 'string' || payloadType === 'array' || payloadType === 'buffer') {
inflight[partId].payload = [];
}
}

View File

@ -69,7 +69,7 @@ describe('SPLIT node', function() {
msg.parts.should.have.property("count",2);
msg.parts.should.have.property("type","array");
msg.parts.should.have.property("index");
msg.payload.should.be.an.Array;
msg.payload.should.be.an.Array();
if (msg.parts.index === 0) { msg.payload.length.should.equal(3); }
if (msg.parts.index === 1) { msg.payload.length.should.equal(1); done(); }
});
@ -213,16 +213,20 @@ describe('SPLIT node', function() {
var sn1 = helper.getNode("sn1");
var sn2 = helper.getNode("sn2");
sn2.on("input", function(msg) {
//console.log(msg);
msg.should.have.property("parts");
msg.payload.should.be.a.Buffer;
msg.parts.should.have.property("count",4);
msg.parts.should.have.property("index");
msg.parts.should.have.property("type","buffer");
if (msg.parts.index === 0) { msg.payload.toString().should.equal("12"); }
if (msg.parts.index === 1) { msg.payload.toString().should.equal("34"); }
if (msg.parts.index === 2) { msg.payload.toString().should.equal("56"); }
if (msg.parts.index === 3) { msg.payload.toString().should.equal("78"); done(); }
try {
//console.log(msg);
msg.should.have.property("parts");
Buffer.isBuffer(msg.payload).should.be.true();
msg.parts.should.have.property("count",4);
msg.parts.should.have.property("index");
msg.parts.should.have.property("type","buffer");
if (msg.parts.index === 0) { msg.payload.toString().should.equal("12"); }
if (msg.parts.index === 1) { msg.payload.toString().should.equal("34"); }
if (msg.parts.index === 2) { msg.payload.toString().should.equal("56"); }
if (msg.parts.index === 3) { msg.payload.toString().should.equal("78"); done(); }
} catch(err) {
done(err);
}
});
var b = new Buffer.from("12345678");
sn1.receive({payload:b});
@ -236,14 +240,17 @@ describe('SPLIT node', function() {
var sn1 = helper.getNode("sn1");
var sn2 = helper.getNode("sn2");
sn2.on("input", function(msg) {
//console.log(msg);
msg.should.have.property("parts");
msg.payload.should.be.a.Buffer;
msg.parts.should.have.property("index");
msg.parts.should.have.property("type","buffer");
if (msg.parts.index === 0) { msg.payload.toString().should.equal("123"); }
if (msg.parts.index === 1) { msg.payload.toString().should.equal("123"); }
if (msg.parts.index === 2) { msg.payload.toString().should.equal("123"); done(); }
try {
msg.should.have.property("parts");
Buffer.isBuffer(msg.payload).should.be.true();
msg.parts.should.have.property("index");
msg.parts.should.have.property("type","buffer");
if (msg.parts.index === 0) { msg.payload.toString().should.equal("123"); }
if (msg.parts.index === 1) { msg.payload.toString().should.equal("123"); }
if (msg.parts.index === 2) { msg.payload.toString().should.equal("123"); done(); }
} catch(err) {
done(err);
}
});
var b1 = new Buffer.from("123412");
var b2 = new Buffer.from("341234");
@ -296,7 +303,26 @@ describe('JOIN node', function() {
n1.receive({payload:"D", parts:{id:1, type:"string", ch:",", index:3, count:4}});
});
});
it('should join bits of string back together automatically with a buffer joiner', function(done) {
var flow = [{id:"n1", type:"join", wires:[["n2"]], joiner:"[44]", joinerType:"bin", build:"string", mode:"auto"},
{id:"n2", type:"helper"}];
helper.load(joinNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
try {
msg.should.have.property("payload");
msg.payload.should.equal("A,B,C,D");
done();
}
catch(e) {done(e);}
});
n1.receive({payload:"A", parts:{id:1, type:"string", ch:",", index:0, count:4}});
n1.receive({payload:"B", parts:{id:1, type:"string", ch:",", index:1, count:4}});
n1.receive({payload:"C", parts:{id:1, type:"string", ch:",", index:2, count:4}});
n1.receive({payload:"D", parts:{id:1, type:"string", ch:",", index:3, count:4}});
});
});
it('should join bits of buffer back together automatically', function(done) {
var flow = [{id:"n1", type:"join", wires:[["n2"]], joiner:",", build:"buffer", mode:"auto"},
{id:"n2", type:"helper"}];
@ -306,7 +332,7 @@ describe('JOIN node', function() {
n2.on("input", function(msg) {
try {
msg.should.have.property("payload");
msg.should.be.a.Buffer;
Buffer.isBuffer(msg.payload).should.be.true();
msg.payload.toString().should.equal("A-B-C-D");
done();
}