1
0
mirror of https://github.com/node-red/node-red.git synced 2023-10-10 13:36:53 +02:00
node-red/nodes/core/logic/17-split.html

542 lines
27 KiB
HTML
Raw Normal View History

2016-06-04 01:40:40 +02:00
<!--
Copyright JS Foundation and other contributors, http://js.foundation
2016-06-04 01:40:40 +02:00
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/x-red" data-template-name="split">
2017-06-13 22:01:04 +02:00
<div class="form-row"><span data-i18n="[html]split.intro"></span></div>
<div class="form-row"><span data-i18n="[html]split.strBuff"></span></div>
2016-06-04 01:40:40 +02:00
<div class="form-row">
<label for="node-input-splt" style="padding-left:10px; margin-right:-10px;" data-i18n="split.splitUsing"></label>
<input type="text" id="node-input-splt" style="width:70%">
2017-06-13 22:01:04 +02:00
<input type="hidden" id="node-input-spltType">
2016-06-04 01:40:40 +02:00
</div>
<div class="form-row">
<input type="checkbox" id="node-input-stream" style="margin-left:10px; vertical-align:top; width:auto;">
2017-06-13 22:01:04 +02:00
<label for="node-input-stream" style="width:auto;" data-i18n="split.stream"></label>
</div>
<div class="form-row"><span data-i18n="[html]split.array"></span></div>
<div class="form-row">
<label for="node-input-arraySplt" style="padding-left:10px; margin-right:-10px;" data-i18n="split.splitUsing"></label>
<input type="text" id="node-input-arraySplt" style="width:70%">
2017-06-13 22:01:04 +02:00
<input type="hidden" id="node-input-arraySpltType">
</div>
<div class="form-row"><span data-i18n="[html]split.object"></span></div>
<div class="form-row" style="padding-left: 10px"><span data-i18n="[html]split.objectSend"></span></div>
<div class="form-row">
2017-06-26 17:51:27 +02:00
<input type="checkbox" id="node-input-addname-cb" style="margin-left:10px; vertical-align:baseline; width:auto;">
<label for="node-input-addname-cb" style="width:auto;" data-i18n="split.addname"></label>
<input type="text" id="node-input-addname" style="width:70%">
</div>
2017-06-13 22:01:04 +02:00
<hr/>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
2016-06-04 01:40:40 +02:00
</div>
</script>
<script type="text/x-red" data-help-name="split">
2017-06-26 17:51:27 +02:00
<p>Splits a message into a sequence of messages.</p>
<h3>Inputs</h3>
<dl class="message-properties">
<dt>payload<span class="property-type">object | string | array | buffer</span></dt>
<dd>The behaviour of the node is determined by the type of <code>msg.payload</code>:
<ul>
2017-10-11 14:31:37 +02:00
<li><b>string</b>/<b>buffer</b> - the message is split using the specified character (default: <code>\n</code>), buffer sequence or into fixed lengths.</li>
2017-06-26 17:51:27 +02:00
<li><b>array</b> - the message is split into either individual array elements, or arrays of a fixed-length.</li>
<li><b>object</b> - a message is sent for each key/value pair of the object.</li>
</ul>
</dd>
</dl>
<h3>Outputs</h3>
<dl class="message-properties">
<dt>parts<span class="property-type">object</span></dt>
<dd>This property contains information about how the message was split from
the original message. If passed to the <b>join</b> node, the sequence can be
reassembled into a single message. The property has the following properties:
<ul>
<li><code>id</code> - an identifier for the group of messages</li>
<li><code>index</code> - the position within the group</li>
<li><code>count</code> - if known, the total number of messages in the group. See 'streaming mode' below.</li>
<li><code>type</code> - the type of message - string/array/object/buffer</li>
<li><code>ch</code> - for a string or buffer, the data used to the split the message as either the string or an array of bytes</li>
<li><code>key</code> - for an object, the key of the property this message was created from. The node can be configured to also copy this value to another message properties, such as <code>msg.topic</code>.</li>
<li><code>len</code> - the length of each message when split using a fixed length value</li>
</ul>
</dd>
</dl>
<h3>Details</h3>
<p>This node makes it easy to create a flow that performs common actions across
a sequence of messages before, using the <b>join</b> node, recombining the
sequence into a single message.</p>
<p>It uses the <code>msg.parts</code> property to track the individual parts
of a sequence.</p>
<h4>Streaming mode</h4>
<p>The node can also be used to reflow a stream of messages. For example, a
serial device that sends newline-terminated commands may deliver a single message
with a partial command at its end. In 'streaming mode', this node will split
a message and send each complete segment. If there is a partial segment at the end,
the node will hold on to it and prepend it to the next message that is received.
</p>
<p>When operating in this mode, the node will not set the `msg.parts.count`
property as it does not know how many messages to expect in the stream. This
means it cannot be used with the <b>join</b> node in its automatic mode</p>
2016-06-04 01:40:40 +02:00
</script>
<script type="text/javascript">
RED.nodes.registerType('split',{
category: 'function',
color:"#E2D96E",
defaults: {
name: {value:""},
2017-06-13 22:01:04 +02:00
splt: {value:"\\n"},
spltType: {value:"str"},
arraySplt: {value:1},
arraySpltType: {value:"len"},
stream: {value:false},
addname: {value:""}
2016-06-04 01:40:40 +02:00
},
inputs:1,
outputs:1,
2016-06-12 13:24:52 +02:00
icon: "split.png",
2016-06-04 01:40:40 +02:00
label: function() {
return this.name||"split";
},
labelStyle: function() {
return this.name?"node_label_italic":"";
2017-06-13 22:01:04 +02:00
},
oneditprepare: function() {
$("#node-input-splt").typedInput({
default: 'str',
typeField: $("#node-input-spltType"),
types:[
'str',
'bin',
{value:"len", label:RED._("node-red:split.splitLength"),validate:/^\d+$/}
]
});
if (this.arraySplt === undefined) {
$("#node-input-arraySplt").val(1);
}
$("#node-input-arraySplt").typedInput({
default: 'len',
typeField: $("#node-input-arraySpltType"),
types:[
{value:"len", label:RED._("node-red:split.splitLength"),validate:/^\d+$/}
]
});
2017-06-26 17:51:27 +02:00
$("#node-input-addname").typedInput({
typeField: $("#node-input-fnameType"),
types:['msg']
});
2017-06-26 17:51:27 +02:00
$("#node-input-addname-cb").change(function() {
$("#node-input-addname").prop('disabled',!this.checked);
})
if (this.addname === "") {
$("#node-input-addname-cb").prop('checked',false);
$("#node-input-addname").val('topic');
} else {
$("#node-input-addname-cb").prop('checked',true);
}
$("#node-input-addname-cb").change();
},
oneditsave: function() {
if (!$("#node-input-addname-cb").prop('checked')) {
$("#node-input-addname").val('');
}
2016-06-04 01:40:40 +02:00
}
});
</script>
2016-06-04 01:40:40 +02:00
<script type="text/x-red" data-template-name="join">
<div class="form-row">
2017-06-13 22:01:04 +02:00
<label data-i18n="join.mode.mode"></label>
2016-06-06 22:18:20 +02:00
<select id="node-input-mode" style="width:200px;">
2017-06-13 22:01:04 +02:00
<option value="auto" data-i18n="join.mode.auto"></option>
<option value="custom" data-i18n="join.mode.custom"></option>
<option value="merge" data-i18n="join.mode.merge"></option>
<option value="reduce" data-i18n="join.mode.reduce"></option>
2016-06-04 01:40:40 +02:00
</select>
</div>
2016-06-06 22:18:20 +02:00
<div class="node-row-custom">
<div class="form-row node-row-property">
2017-06-13 22:01:04 +02:00
<label data-i18n="join.combine"> </label>
<input type="text" id="node-input-property" style="width:70%;">
2016-06-06 22:18:20 +02:00
<input type="hidden" id="node-input-propertyType">
</div>
<div class="form-row">
2017-06-13 22:01:04 +02:00
<label data-i18n="join.create"></label>
<select id="node-input-build" style="width:70%;">
2017-06-27 18:10:52 +02:00
<option value="string" data-i18n="join.type.string"></option>
<option value="buffer" data-i18n="join.type.buffer"></option>
2017-06-13 22:01:04 +02:00
<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>
2016-06-06 22:18:20 +02:00
</select>
</div>
<div class="form-row node-row-key">
2017-06-27 18:10:52 +02:00
<label style="vertical-align:top; margin-top:7px; width:auto; margin-right: 5px;" data-i18n="join.using"></label>
2016-06-06 22:18:20 +02:00
<div style="display:inline-block">
2017-06-27 18:10:52 +02:00
<input type="text" id="node-input-key" style="width:220px;"> <span data-i18n="join.key"></span>
2016-06-06 22:18:20 +02:00
</div>
</div>
<div class="form-row node-row-joiner">
2017-06-13 22:01:04 +02:00
<label for="node-input-joiner" data-i18n="join.joinedUsing"></label>
2017-06-27 18:10:52 +02:00
<input type="text" id="node-input-joiner" style="width:70%">
<input type="hidden" id="node-input-joinerType">
2016-06-06 22:18:20 +02:00
</div>
<div class="form-row node-row-trigger" id="trigger-row">
2017-06-13 22:01:04 +02:00
<label style="width:auto;" data-i18n="join.send"></label>
2016-06-06 22:18:20 +02:00
<ul>
<li>
2017-06-13 22:01:04 +02:00
<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>
2017-06-27 18:10:52 +02:00
<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>
2016-06-06 22:18:20 +02:00
</li>
<li>
2017-06-13 22:01:04 +02:00
<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;">
2016-06-06 22:18:20 +02:00
</li>
<li>
2017-06-13 22:01:04 +02:00
<label style="width:auto; padding-top:6px;" data-i18n="[html]join.complete"></label>
2016-06-06 22:18:20 +02:00
</li>
</ul>
</div>
2016-06-04 01:40:40 +02:00
</div>
<div class="node-row-merge">
<div class="form-row">
<label data-i18n="join.merge.topics-label"></label>
<div class="form-row node-input-topics-container-row">
<ol id="node-input-topics-container"></ol>
</div>
</div>
<div class="form-row">
<input type="checkbox" id="node-input-mergeOnChange" style="margin-left:10px; vertical-align:top; width:auto;">
<label for="node-input-mergeOnChange" style="width:auto;" data-i18n="join.merge.on-change"></label>
</div>
</div>
<div class="node-row-reduce">
<div class="form-row">
<label for="node-input-reduceExp" data-i18n="join.reduce.exp" style="margin-left:10px;"></label>
<input type="text" id="node-input-reduceExp" data-i18n="[placeholder]join.reduce.exp-value" style="width:65%">
</div>
<div class="form-row">
<label for="node-input-reduceInit" data-i18n="join.reduce.init" style="margin-left:10px;"></label>
<input type="text" id="node-input-reduceInit" data-i18n="[placeholder]join.reduce.init" style="width:65%">
<input type="hidden" id="node-input-reduceInitType">
</div>
<div class="form-row">
<label for="node-input-reduceFixup" data-i18n="join.reduce.fixup" style="margin-left:10px;"></label>
<input type="text" id="node-input-reduceFixup" data-i18n="[placeholder]join.reduce.exp-value" style="width:65%">
</div>
<div class="form-row">
<label>&nbsp;</label>
<input type="checkbox" id="node-input-reduceRight" style="display:inline-block; width:auto; vertical-align:top; margin-left:10px;">
<label for="node-input-reduceRight" style="width:70%;" data-i18n="join.reduce.right" style="margin-left:10px;"/>
</div>
</div>
<div class="form-row">
2017-06-13 22:01:04 +02:00
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
</div>
2017-06-13 22:01:04 +02:00
<div class="form-tips form-tips-auto hide" data-i18n="[html]join.tip"></div>
2016-06-04 01:40:40 +02:00
</script>
<script type="text/x-red" data-help-name="join">
<p>Joins sequences of messages into a single message. This node provides four mode for message combination:</p>
<dl>
<dt>automatic</dt>
<dd>When paired with the <b>split</b> node, it will automatically join the messages to reverse the split that was performed.</dd>
<dt>manual</dt>
<dd>It will join sequences of messages in a variety of ways.</dd>
<dt>merge sequence</dt>
<dd>It will merge incoming messages into single message using <code>topic</code> property.</dd>
<dt>reduce sequence</dt>
<dd>When paired with the <b>split</b> node, it will reduce the message sequence into single message.</dd>
</dl>
2017-06-13 22:01:04 +02:00
<h3>Inputs</h3>
<dl class="message-properties">
<dt class="optional">parts<span class="property-type">object</span></dt>
<dd>To automatically join a sequence of messages, they should all have
this property set. The <b>split</b> node generates this property but it
can be manually created. It has the following properties:
<ul>
<li><code>id</code> - an identifier for the group of messages</li>
<li><code>index</code> - the position within the group</li>
<li><code>count</code> - the total number of messages in the group</li>
2017-06-26 17:51:27 +02:00
<li><code>type</code> - the type of message - string/array/object/buffer</li>
<li><code>ch</code> - for a string or buffer, the data used to the split the message as either the string or an array of bytes</li>
2017-06-13 22:01:04 +02:00
<li><code>key</code> - for an object, the key of the property this message was created from</li>
2017-06-26 17:51:27 +02:00
<li><code>len</code> - the length of each message when split using a fixed length value</li>
2017-06-13 22:01:04 +02:00
</ul>
</dd>
<dt class="optional">complete</dt>
<dd>If set, the node will send its output message in its current state.</dd>
</dl>
<h3>Details</h3>
<h4>Automatic mode</h4>
<p>When configured to join in manual mode, the node is able to join sequences of messages using <code>parts</code> property of incoming messages.</p>
<h4>Manual mode</h4>
2017-06-13 22:01:04 +02:00
<p>When configured to join in manual mode, the node is able to join sequences
of messages in a variety of ways.</p>
2016-06-11 22:43:37 +02:00
<ul>
2017-06-27 18:10:52 +02:00
<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>
2016-06-11 22:43:37 +02:00
<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>
2017-06-13 22:01:04 +02:00
<p>The other properties of the output message are taken from the last message received before the result is sent.</p>
2017-06-27 18:10:52 +02:00
<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>
2016-06-10 23:51:57 +02:00
<p>A <i>timeout</i> can be set to trigger sending the new message using whatever has been received so far.</p>
2016-06-11 22:43:37 +02:00
<p>If a message is received with the <b>msg.complete</b> property set, the output message is sent.</p>
<h4>Merge Sequence mode</h4>
<p>When configured to join in merge mode, the join node can create a message based on <code>topic</code> value.</p>
<p>Input messages are merged in order specified by <b>Topics</b> value.
<p>For example, if value of <b>Topics</b> is <b>x,x,y</b>, two input messages with topic <b>x</b> and one input message with topic <b>y</b> are merged into a new message in order of arrival.</p>
<p>If "Send merged message on arrival of a new topic" check box is selected, the last messages with each topic is kept internally and output message is sent when a message with new topics arrives.</p>
<p>The merged message contains <code>payload</code> property and properties for each topic. The <code>payload</code> property represents ordered array of payload value of input messages for each topic. The property for each topic represents a payload value for single occurrence of topic or array of payload values for multiple occurrences of the topic.</p>
<h4>Reduce Sequence mode</h4>
<p>When configured to join in reduce sequence mode, following values can be specified:</p>
<dl class="message-properties">
<dt>Reduce exp</dt>
<dd>JSONata expression for reducing message group. This expression represents the accumulated result. In the expression, following special variables can be used:
<ul>
<li><code>$A</code> accumulated value, </li>
<li><code>$I</code> index of the message in a group, </li>
<li><code>$N</code> number of messages of a group.</li>
</ul>
</dd>
<dt>Initial value</dt>
<dd>
initial value of reduction.
</dd>
<dt>Fixup exp</dt>
<dd>
JSONata expression applied after reduction of a message group completed. In the expression, following special variables can be used:
<ul>
<li><code>$A</code> accumulated value, </li>
<li><code>$N</code> number of messages of a group.</li>
</ul>
</dd>
<p>Order of reduction on a message group can be specified by checkbox (<b>Evaluate in reverse order (right to left)</b>).</p>
</dl>
<p><b>Example:</b> Join node outputs an average for each input message group with the following setting:
<ul>
<li><b>Reduce exp</b>: <code>$A+payload</code></li>
<li><b>Initial value</b>: <code>0</code></li>
<li><b>Fixup exp</b>: <code>$A/$N</code></li>
</ul>
</p>
<h4>Note:</h4>
<p>This node internally keeps messages for its operation. In order to prevent unexpected memory usage, maximum number of messages kept can be specified by <code>joinMaxKeptMsgsCount</code> property in <b>settings.js</b>.</p>
2016-06-04 01:40:40 +02:00
</script>
<script type="text/javascript">
RED.nodes.registerType('join',{
category: 'function',
color:"#E2D96E",
defaults: {
name: {value:""},
2016-06-06 22:18:20 +02:00
mode: {value:"auto"},
build: { value:"string"},
property: { value:"payload", validate:RED.validators.typedInput("propertyType")},
propertyType: { value:"msg"},
key: {value:"topic"},
joiner: { value:"\\n"},
2017-06-27 18:10:52 +02:00
joinerType: { value:"str"},
accumulate: { value:"false" },
2016-06-04 01:40:40 +02:00
timeout: {value:""},
count: {value:""},
topics: {value:[{topic:""}]},
mergeOnChange: {value:false},
reduceRight: {value:false},
reduceExp: {value:undefined},
reduceInit: {value:undefined},
reduceInitType: {value:undefined},
reduceFixup: {value:undefined}
2016-06-04 01:40:40 +02:00
},
inputs:1,
outputs:1,
icon: "join.png",
label: function() {
return this.name||"join";
},
labelStyle: function() {
return this.name?"node_label_italic":"";
},
oneditprepare: function() {
var node = this;
var topic_str = node._("join.merge.topic");
function resizeTopics(topic) {
var newWidth = topic.width();
topic.find('.red-ui-typedInput')
.typedInput("width",newWidth-15);
}
$("#node-input-topics-container")
.css('min-height','250px').css('min-width','420px')
.editableList({
addItem: function(container,i,opt) {
if (!opt.hasOwnProperty('topic')) {
opt.topic = "";
}
var row = $('<div/>').appendTo(container);
var valueField = $('<input/>',{
class:"node-input-topic-value",
type:"text",
style:"margin-left: 5px;"
}).appendTo(row)
.typedInput({default:'str', types:['str']});
valueField.typedInput('value', opt.topic);
valueField.typedInput('type', 'str');
valueField.attr('placeholder', topic_str);
resizeTopics(container);
},
resizeItem: resizeTopics,
sortable: true,
removable: true
});
2016-06-06 22:18:20 +02:00
$("#node-input-mode").change(function(e) {
var val = $(this).val();
$(".node-row-custom").toggle(val==='custom');
$(".node-row-merge").toggle(val==='merge');
$(".node-row-reduce").toggle(val==='reduce');
$(".form-tips-auto").toggle((val==='auto') || (val==='reduce'));
if (val === "auto") {
$("#node-input-accumulate").attr('checked', false);
}
else if (val === "merge") {
var topics = node.topics;
var container = $("#node-input-topics-container");
container.editableList('empty');
for (var i=0;i<topics.length;i++) {
var topic = topics[i];
container.editableList('addItem', topic);
}
}
else if (val === "reduce") {
var jsonata_or_empty = {
value: "jsonata",
label: "expression",
icon: "red/images/typedInput/expr.png",
validate: function(v) {
try{
if(v !== "") {
jsonata(v);
}
return true;
}
catch(e){
return false;
}
},
expand:function() {
var that = this;
RED.editor.editExpression({
value: this.value().replace(/\t/g,"\n"),
complete: function(v) {
that.value(v.replace(/\n/g,"\t"));
}
})
}
};
$("#node-input-reduceExp").typedInput({types:[jsonata_or_empty]});
$("#node-input-reduceInit").typedInput({
default: 'num',
types:['flow','global','str','num','bool','json','bin','date','jsonata'],
typeField: $("#node-input-reduceInitType")
});
$("#node-input-reduceFixup").typedInput({types:[jsonata_or_empty]});
}
2016-06-06 22:18:20 +02:00
});
$("#node-input-build").change(function(e) {
2016-06-04 01:40:40 +02:00
var val = $(this).val();
2017-06-13 22:01:04 +02:00
$(".node-row-key").toggle(val==='object');
2017-06-27 18:10:52 +02:00
$(".node-row-accumulate").toggle(val==='object' || val==='merged');
$(".node-row-joiner").toggle(val==='string' || val==='buffer');
2016-06-04 01:40:40 +02:00
$(".node-row-trigger").toggle(val!=='auto');
2017-06-27 18:10:52 +02:00
if (val === 'string' || val==='buffer') {
$("#node-input-property").typedInput('types',['msg']);
2016-06-06 22:18:20 +02:00
} else {
$("#node-input-property").typedInput('types',['msg', {value:"full",label:"complete message",hasValue:false}]);
}
});
2016-06-04 01:40:40 +02:00
2017-06-27 18:10:52 +02:00
$("#node-input-joiner").typedInput({
default: 'str',
typeField: $("#node-input-joinerType"),
types:[
'str',
'bin'
]
});
2016-06-04 01:40:40 +02:00
$("#node-input-property").typedInput({
typeField: $("#node-input-propertyType"),
types:['msg', {value:"full", label:"complete message", hasValue:false}]
});
2016-06-04 01:40:40 +02:00
$("#node-input-key").typedInput({
types:['msg', {value:"merge", label:"", hasValue:false}]
});
2016-06-04 01:40:40 +02:00
$("#node-input-build").change();
2016-06-06 22:18:20 +02:00
$("#node-input-mode").change();
2017-06-27 18:10:52 +02:00
},
oneditsave: function() {
var build = $("#node-input-build").val();
if (build !== 'object' && build !== 'merged') {
$("#node-input-accumulate").prop("checked",false);
}
var topics = $("#node-input-topics-container").editableList('items');
var node = this;
node.topics = [];
topics.each(function(i) {
var topicData = $(this).data('data');
var topic = $(this);
var vf = topic.find(".node-input-topic-value");
var value = vf.typedInput('value');
var r = {topic:value};
node.topics.push(r);
});
},
oneditresize: function(size) {
var rows = $("#dialog-form>div:not(.node-input-topics-container-row)");
var height = size.height;
for (var i=0;i<rows.size();i++) {
height -= $(rows[i]).outerHeight(true);
}
var editorRow = $("#dialog-form>div.node-input-topics-container-row");
height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
$("#node-input-topics-container").editableList('height',height);
2016-06-04 01:40:40 +02:00
}
});
</script>