Merge branch 'dev' into Fix-join-to-ignore-parts-in-manual-mode

This commit is contained in:
Nick O'Leary
2024-03-07 16:05:41 +00:00
committed by GitHub
170 changed files with 9308 additions and 1498 deletions

View File

@@ -320,7 +320,7 @@
}
// but replace with repeat one if set to repeat
if ((this.repeat && this.repeat != 0) || this.crontab) {
suffix = " ↻";
suffix = "\t↻";
}
if (this.name) {
return this.name+suffix;

View File

@@ -109,9 +109,8 @@ module.exports = function(RED) {
}
const p = props.shift()
const property = p.p;
const value = p.v ? p.v : '';
const valueType = p.vt ? p.vt : 'str';
const value = p.v !== undefined ? p.v : '';
const valueType = p.vt !== undefined ? p.vt : 'str';
if (property) {
if (valueType === "jsonata") {
if (p.v) {

View File

@@ -86,7 +86,7 @@
},
label: function() {
var suffix = "";
if (this.console === true || this.console === "true") { suffix = " ⇲"; }
if (this.console === true || this.console === "true") { suffix = "\t⇲"; }
if (this.targetType === "jsonata") {
return (this.name || "JSONata") + suffix;
}
@@ -195,6 +195,119 @@
node.dirty = true;
});
RED.view.redraw();
},
requestDebugNodeList: function(filteredNodes) {
var workspaceOrder = RED.nodes.getWorkspaceOrder();
var workspaceOrderMap = {};
workspaceOrder.forEach(function(ws,i) {
workspaceOrderMap[ws] = i;
});
var candidateNodes = [];
var candidateSFs = [];
var subflows = {};
RED.nodes.eachNode(function (n) {
var nt = n.type;
if (nt === "debug") {
if (n.z in workspaceOrderMap) {
candidateNodes.push(n);
}
else {
var sf = RED.nodes.subflow(n.z);
if (sf) {
subflows[sf.id] = {
debug: true,
subflows: {}
};
}
}
}
else if(nt.substring(0, 8) === "subflow:") {
if (n.z in workspaceOrderMap) {
candidateSFs.push(n);
}
else {
var psf = RED.nodes.subflow(n.z);
if (psf) {
var sid = nt.substring(8);
var item = subflows[psf.id];
if (!item) {
item = {
debug: undefined,
subflows: {}
};
subflows[psf.id] = item;
}
item.subflows[sid] = true;
}
}
}
});
candidateSFs.forEach(function (sf) {
var sid = sf.type.substring(8);
if (containsDebug(sid, subflows)) {
candidateNodes.push(sf);
}
});
candidateNodes.sort(function(A,B) {
var wsA = workspaceOrderMap[A.z];
var wsB = workspaceOrderMap[B.z];
if (wsA !== wsB) {
return wsA-wsB;
}
var labelA = RED.utils.getNodeLabel(A,A.id);
var labelB = RED.utils.getNodeLabel(B,B.id);
return labelA.localeCompare(labelB);
});
var currentWs = null;
var data = [];
var currentFlow;
var currentSelectedCount = 0;
candidateNodes.forEach(function(node) {
if (currentWs !== node.z) {
if (currentFlow && currentFlow.checkbox) {
currentFlow.selected = currentSelectedCount === currentFlow.children.length
}
currentSelectedCount = 0;
currentWs = node.z;
var parent = RED.nodes.workspace(currentWs) || RED.nodes.subflow(currentWs);
currentFlow = {
label: RED.utils.getNodeLabel(parent, currentWs),
}
if (!parent.disabled) {
currentFlow.children = [];
currentFlow.checkbox = true;
} else {
currentFlow.class = "disabled"
}
data.push(currentFlow);
}
if (currentFlow.children) {
if (!filteredNodes[node.id]) {
currentSelectedCount++;
}
currentFlow.children.push({
label: RED.utils.getNodeLabel(node,node.id),
node: {
id: node.id
},
checkbox: true,
selected: !filteredNodes[node.id]
});
}
});
if (currentFlow && currentFlow.checkbox) {
currentFlow.selected = currentSelectedCount === currentFlow.children.length
}
if (subWindow) {
try {
subWindow.postMessage({event:"refreshDebugNodeList", nodes:data},"*");
} catch(err) {
console.log(err);
}
}
RED.debug.refreshDebugNodeList(data)
}
};
@@ -396,6 +509,26 @@
}
}
function containsDebug(sid, map) {
var item = map[sid];
if (item) {
if (item.debug === undefined) {
var sfs = Object.keys(item.subflows);
var contain = false;
for (var i = 0; i < sfs.length; i++) {
var sf = sfs[i];
if (containsDebug(sf, map)) {
contain = true;
break;
}
}
item.debug = contain;
}
return item.debug;
}
return false;
}
$("#red-ui-sidebar-debug-open").on("click", function(e) {
e.preventDefault();
subWindow = window.open(document.location.toString().replace(/[?#].*$/,"")+"debug/view/view.html"+document.location.search,"nodeREDDebugView","menubar=no,location=no,toolbar=no,chrome,height=500,width=600");
@@ -427,6 +560,8 @@
options.messageSourceClick(msg.id,msg._alias,msg.path);
} else if (msg.event === "clear") {
options.clear();
} else if (msg.event === "requestDebugNodeList") {
options.requestDebugNodeList(msg.filteredNodes)
}
};
window.addEventListener('message',this.handleWindowMessage);

View File

@@ -275,7 +275,7 @@
value: [],
type: "link in[]",
validate: function (v, opt) {
if ((this.linkType === "static" && v.length > 0)
if (((this.linkType || "static") === "static" && v.length > 0)
|| this.linkType === "dynamic") {
return true;
}

View File

@@ -167,19 +167,13 @@ RED.debug = (function() {
var menu = RED.popover.menu({
options: options,
onselect: function(item) {
if (item.value !== filterType) {
filterType = item.value;
$('#red-ui-sidebar-debug-filter span').text(RED._('node-red:debug.sidebar.'+filterType));
refreshMessageList();
RED.settings.set("debug.filter",filterType)
}
setFilterType(item.value)
if (filterType === 'filterSelected') {
refreshDebugNodeList();
config.requestDebugNodeList(filteredNodes);
filterDialog.slideDown(200);
filterDialogShown = true;
debugNodeTreeList.focus();
}
}
});
menu.show({
@@ -254,131 +248,7 @@ RED.debug = (function() {
}
function containsDebug(sid, map) {
var item = map[sid];
if (item) {
if (item.debug === undefined) {
var sfs = Object.keys(item.subflows);
var contain = false;
for (var i = 0; i < sfs.length; i++) {
var sf = sfs[i];
if (containsDebug(sf, map)) {
contain = true;
break;
}
}
item.debug = contain;
}
return item.debug;
}
return false;
}
function refreshDebugNodeList() {
var workspaceOrder = RED.nodes.getWorkspaceOrder();
var workspaceOrderMap = {};
workspaceOrder.forEach(function(ws,i) {
workspaceOrderMap[ws] = i;
});
var candidateNodes = [];
var candidateSFs = [];
var subflows = {};
RED.nodes.eachNode(function (n) {
var nt = n.type;
if (nt === "debug") {
if (n.z in workspaceOrderMap) {
candidateNodes.push(n);
}
else {
var sf = RED.nodes.subflow(n.z);
if (sf) {
subflows[sf.id] = {
debug: true,
subflows: {}
};
}
}
}
else if(nt.substring(0, 8) === "subflow:") {
if (n.z in workspaceOrderMap) {
candidateSFs.push(n);
}
else {
var psf = RED.nodes.subflow(n.z);
if (psf) {
var sid = nt.substring(8);
var item = subflows[psf.id];
if (!item) {
item = {
debug: undefined,
subflows: {}
};
subflows[psf.id] = item;
}
item.subflows[sid] = true;
}
}
}
});
candidateSFs.forEach(function (sf) {
var sid = sf.type.substring(8);
if (containsDebug(sid, subflows)) {
candidateNodes.push(sf);
}
});
candidateNodes.sort(function(A,B) {
var wsA = workspaceOrderMap[A.z];
var wsB = workspaceOrderMap[B.z];
if (wsA !== wsB) {
return wsA-wsB;
}
var labelA = RED.utils.getNodeLabel(A,A.id);
var labelB = RED.utils.getNodeLabel(B,B.id);
return labelA.localeCompare(labelB);
});
var currentWs = null;
var data = [];
var currentFlow;
var currentSelectedCount = 0;
candidateNodes.forEach(function(node) {
if (currentWs !== node.z) {
if (currentFlow && currentFlow.checkbox) {
currentFlow.selected = currentSelectedCount === currentFlow.children.length
}
currentSelectedCount = 0;
currentWs = node.z;
var parent = RED.nodes.workspace(currentWs) || RED.nodes.subflow(currentWs);
currentFlow = {
label: RED.utils.getNodeLabel(parent, currentWs),
}
if (!parent.disabled) {
currentFlow.children = [];
currentFlow.checkbox = true;
} else {
currentFlow.class = "disabled"
}
data.push(currentFlow);
}
if (currentFlow.children) {
if (!filteredNodes[node.id]) {
currentSelectedCount++;
}
currentFlow.children.push({
label: RED.utils.getNodeLabel(node,node.id),
node: node,
checkbox: true,
selected: !filteredNodes[node.id]
});
}
});
if (currentFlow && currentFlow.checkbox) {
currentFlow.selected = currentSelectedCount === currentFlow.children.length
}
function refreshDebugNodeList(data) {
debugNodeTreeList.treeList("data", data);
}
@@ -401,7 +271,7 @@ RED.debug = (function() {
},200);
}
function _refreshMessageList(_activeWorkspace) {
if (_activeWorkspace) {
if (typeof _activeWorkspace === 'string') {
activeWorkspace = _activeWorkspace.replace(/\./g,"_");
}
if (filterType === "filterAll") {
@@ -479,12 +349,12 @@ RED.debug = (function() {
filteredNodes[n.id] = true;
});
delete filteredNodes[sourceId];
$("#red-ui-sidebar-debug-filterSelected").trigger("click");
RED.settings.set('debug.filteredNodes',Object.keys(filteredNodes))
setFilterType('filterSelected')
refreshMessageList();
}},
{id:"red-ui-debug-msg-menu-item-clear-filter",label:RED._("node-red:debug.messageMenu.clearFilter"),onselect:function(){
$("#red-ui-sidebar-debug-filterAll").trigger("click");
clearFilterSettings()
refreshMessageList();
}}
);
@@ -713,9 +583,17 @@ RED.debug = (function() {
if (!!clearFilter) {
clearFilterSettings();
}
refreshDebugNodeList();
config.requestDebugNodeList(filteredNodes);
}
function setFilterType(type) {
if (type !== filterType) {
filterType = type;
$('#red-ui-sidebar-debug-filter span').text(RED._('node-red:debug.sidebar.'+filterType));
refreshMessageList();
RED.settings.set("debug.filter",filterType)
}
}
function clearFilterSettings() {
filteredNodes = {};
filterType = 'filterAll';
@@ -728,6 +606,7 @@ RED.debug = (function() {
init: init,
refreshMessageList:refreshMessageList,
handleDebugMessage: handleDebugMessage,
clearMessageList: clearMessageList
clearMessageList: clearMessageList,
refreshDebugNodeList: refreshDebugNodeList
}
})();

View File

@@ -12,6 +12,9 @@ $(function() {
},
clear: function() {
window.opener.postMessage({event:"clear"},'*');
},
requestDebugNodeList: function(filteredNodes) {
window.opener.postMessage({event: 'requestDebugNodeList', filteredNodes},'*')
}
}
@@ -26,6 +29,8 @@ $(function() {
RED.debug.refreshMessageList(evt.data.activeWorkspace);
} else if (evt.data.event === "projectChange") {
RED.debug.clearMessageList(true);
} else if (evt.data.event === "refreshDebugNodeList") {
RED.debug.refreshDebugNodeList(evt.data.nodes)
}
},false);
} catch(err) {

View File

@@ -315,7 +315,7 @@ module.exports = function(RED) {
var spec = module.module;
if (spec && (spec !== "")) {
moduleLoadPromises.push(RED.import(module.module).then(lib => {
sandbox[vname] = lib.default;
sandbox[vname] = lib.default || lib;
}).catch(err => {
node.error(RED._("function.error.moduleLoadError",{module:module.spec, error:err.toString()}))
throw err;

View File

@@ -103,7 +103,6 @@
} else if (type === "istype") {
r.v = rule.find(".node-input-rule-type-value").typedInput('type');
r.vt = rule.find(".node-input-rule-type-value").typedInput('type');
r.vt = (r.vt === "number") ? "num" : "str";
} else if (type === "jsonata_exp") {
r.v = rule.find(".node-input-rule-exp-value").typedInput('value');
r.vt = rule.find(".node-input-rule-exp-value").typedInput('type');
@@ -168,7 +167,35 @@
label:RED._("node-red:common.label.payload"),
validate: RED.validators.typedInput("propertyType", false)},
propertyType: { value:"msg" },
rules: {value:[{t:"eq", v:"", vt:"str"}]},
rules: {
value:[{t:"eq", v:"", vt:"str"}],
validate: function (rules, opt) {
let msg;
const errors = []
if (!rules || rules.length === 0) { return true }
for (var i=0;i<rules.length;i++) {
const opt = { label: RED._('node-red:switch.label.rule')+' '+(i+1) }
const r = rules[i];
if (r.t !== 'istype') {
if (r.hasOwnProperty('v')) {
if ((msg = RED.utils.validateTypedProperty(r.v,r.vt,opt)) !== true) {
errors.push(msg)
}
}
if (r.hasOwnProperty('v2')) {
if ((msg = RED.utils.validateTypedProperty(r.v2,r.v2t,opt)) !== true) {
errors.push(msg)
}
}
}
}
if (errors.length) {
console.log(errors)
return errors
}
return true;
}
},
checkall: {value:"true", required:true},
repair: {value:false},
outputs: {value:1}
@@ -218,7 +245,11 @@
if (i > 0) {
var lastRule = $("#node-input-rule-container").editableList('getItemAt',i-1);
var exportedRule = exportRule(lastRule.element);
opt.r.vt = exportedRule.vt;
if (exportedRule.t === "istype") {
opt.r.vt = (exportedRule.vt === "number") ? "num" : "str";
} else {
opt.r.vt = exportedRule.vt;
}
opt.r.v = "";
// We could copy the value over as well and preselect it (see the 'activeElement' code below)
// But not sure that feels right. Is copying over the last value 'expected' behaviour?

View File

@@ -19,71 +19,42 @@
<script type="text/javascript">
(function() {
function isInvalidProperty(v,vt) {
if (/msg|flow|global/.test(vt)) {
if (!RED.utils.validatePropertyExpression(v)) {
return RED._("node-red:change.errors.invalid-prop", {
property: v
});
}
} else if (vt === "jsonata") {
try{ jsonata(v); } catch(e) {
return RED._("node-red:change.errors.invalid-expr", {
error: e.message
});
}
} else if (vt === "json") {
try{ JSON.parse(v); } catch(e) {
return RED._("node-red:change.errors.invalid-json-data", {
error: e.message
});
}
}
return false;
}
RED.nodes.registerType('change', {
color: "#E2D96E",
category: 'function',
defaults: {
name: {value:""},
rules:{value:[{t:"set",p:"payload",pt:"msg",to:"",tot:"str"}],validate: function(rules, opt) {
var msg;
if (!rules || rules.length === 0) { return true }
for (var i=0;i<rules.length;i++) {
var r = rules[i];
if (r.t === 'set') {
if (msg = isInvalidProperty(r.p,r.pt)) {
return msg;
rules:{
value:[{t:"set",p:"payload",pt:"msg",to:"",tot:"str"}],
validate: function(rules, opt) {
let msg;
const errors = []
if (!rules || rules.length === 0) { return true }
for (var i=0;i<rules.length;i++) {
const opt = { label: RED._('node-red:change.label.rule')+' '+(i+1) }
const r = rules[i];
if (r.t === 'set' || r.t === 'change' || r.t === 'delete' || r.t === 'move') {
if ((msg = RED.utils.validateTypedProperty(r.p,r.pt,opt)) !== true) {
errors.push(msg)
}
}
if (msg = isInvalidProperty(r.to,r.tot)) {
return msg;
if (r.t === 'set' || r.t === 'change' || r.t === 'move') {
if ((msg = RED.utils.validateTypedProperty(r.to,r.tot,opt)) !== true) {
errors.push(msg)
}
}
} else if (r.t === 'change') {
if (msg = isInvalidProperty(r.p,r.pt)) {
return msg;
}
if(msg = isInvalidProperty(r.from,r.fromt)) {
return msg;
}
if(msg = isInvalidProperty(r.to,r.tot)) {
return msg;
}
} else if (r.t === 'delete') {
if (msg = isInvalidProperty(r.p,r.pt)) {
return msg;
}
} else if (r.t === 'move') {
if (msg = isInvalidProperty(r.p,r.pt)) {
return msg;
}
if (msg = isInvalidProperty(r.to,r.tot)) {
return msg;
if (r.t === 'change') {
if ((msg = RED.utils.validateTypedProperty(r.from,r.fromt,opt)) !== true) {
errors.push(msg)
}
}
}
if (errors.length) {
return errors
}
return true;
}
return true;
}},
},
// legacy
action: {value:""},
property: {value:""},

View File

@@ -117,7 +117,7 @@ module.exports = function(RED) {
});
return
} else if (rule.tot === 'date') {
value = Date.now();
value = RED.util.evaluateNodeProperty(rule.to, rule.tot, node)
} else if (rule.tot === 'jsonata') {
RED.util.evaluateJSONataExpression(rule.to,msg, (err, value) => {
if (err) {
@@ -233,7 +233,9 @@ module.exports = function(RED) {
// only replace if they match exactly
RED.util.setMessageProperty(msg,property,value);
} else {
current = current.replace(fromRE,value);
// if target is boolean then just replace it
if (rule.tot === "bool") { current = value; }
else { current = current.replace(fromRE,value); }
RED.util.setMessageProperty(msg,property,current);
}
} else if ((typeof current === 'number' || current instanceof Number) && fromType === 'num') {

View File

@@ -57,7 +57,9 @@
action: {value:"scale"},
round: {value:false},
property: {value:"payload",required:true,
label:RED._("node-red:common.label.property")},
label:RED._("node-red:common.label.property"),
validate: RED.validators.typedInput({ type: 'msg', allowBlank: true })
},
name: {value:""}
},
inputs: 1,

View File

@@ -153,7 +153,7 @@
}
var editorRow = $("#dialog-form>div.node-text-editor-row");
height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
$(".node-text-editor").css("height",height+"px");
$("#dialog-form .node-text-editor").css("height",height+"px");
this.editor.resize();
}
});

View File

@@ -284,7 +284,7 @@ module.exports = function(RED) {
done();
}
}
else {
else if (!msg.hasOwnProperty("reset")) {
if (maxKeptMsgsCount(node) > 0) {
if (node.intervalID === -1) {
node.send(msg);

View File

@@ -56,7 +56,7 @@
color:"darksalmon",
defaults: {
command: {value:""},
addpay: {value:""},
addpay: {value:"", validate: RED.validators.typedInput({ type: 'msg', allowBlank: true })},
append: {value:""},
useSpawn: {value:"false"},
timer: {value:""},

View File

@@ -56,9 +56,11 @@
inout: {value:"out"},
septopics: {value:true},
property: {value:"payload", required:true,
label:RED._("node-red:rbe.label.property")},
label:RED._("node-red:rbe.label.property"),
validate: RED.validators.typedInput({ type: 'msg', allowUndefined: true })},
topi: {value:"topic", required:true,
label:RED._("node-red:rbe.label.topic")}
label:RED._("node-red:rbe.label.topic"),
validate: RED.validators.typedInput({ type: 'msg', allowUndefined: true })}
},
inputs:1,
outputs:1,

View File

@@ -104,6 +104,7 @@ module.exports = function(RED) {
* @returns `true` if it is a valid topic
*/
function isValidPublishTopic(topic) {
if (topic.length === 0) return false;
return !/[\+#\b\f\n\r\t\v\0]/.test(topic);
}
@@ -219,8 +220,8 @@ module.exports = function(RED) {
*/
function subscriptionHandler(node, datatype ,topic, payload, packet) {
const msg = {topic:topic, payload:null, qos:packet.qos, retain:packet.retain};
const v5 = (node && node.brokerConn)
? node.brokerConn.v5()
const v5 = (node && node.brokerConn)
? node.brokerConn.v5()
: Object.prototype.hasOwnProperty.call(packet, "properties");
if(v5 && packet.properties) {
setStrProp(packet.properties, msg, "responseTopic");
@@ -451,7 +452,7 @@ module.exports = function(RED) {
/**
* Perform the disconnect action
* @param {MQTTInNode|MQTTOutNode} node
* @param {MQTTInNode|MQTTOutNode} node
* @param {Function} done
*/
function handleDisconnectAction(node, done) {
@@ -611,7 +612,7 @@ module.exports = function(RED) {
node.brokerurl = node.url;
} else {
// if the broker is ws:// or wss:// or tcp://
if (node.broker.indexOf("://") > -1) {
if ((typeof node.broker === 'string') && node.broker.indexOf("://") > -1) {
node.brokerurl = node.broker;
// Only for ws or wss, check if proxy env var for additional configuration
if (node.brokerurl.indexOf("wss://") > -1 || node.brokerurl.indexOf("ws://") > -1) {
@@ -865,7 +866,7 @@ module.exports = function(RED) {
* Call end and wait for the client to end (or timeout)
* @param {mqtt.MqttClient} client The broker client
* @param {number} ms The time to wait for the client to end
* @returns
* @returns
*/
let waitEnd = (client, ms) => {
return new Promise( (resolve, reject) => {
@@ -905,7 +906,7 @@ module.exports = function(RED) {
node.subid = 1;
//typedef for subscription object:
/**
/**
* @typedef {Object} Subscription
* @property {String} topic - topic to subscribe to
* @property {Object} [options] - options object
@@ -933,7 +934,7 @@ module.exports = function(RED) {
const ref = _ref || 0;
let options
let qos = 1 // default to QoS 1 (AWS and several other brokers don't support QoS 2)
// if options is an object, then clone it
if (typeof _options == "object") {
options = RED.util.cloneMessage(_options || {})
@@ -947,7 +948,7 @@ module.exports = function(RED) {
if (typeof qos === "number" && qos >= 0 && qos <= 2) {
options.qos = qos;
}
subscription.topic = _topic;
subscription.qos = qos;
subscription.options = RED.util.cloneMessage(options);
@@ -957,16 +958,16 @@ module.exports = function(RED) {
}
/**
* If topic is a subscription object, then use that, otherwise look up the topic in
* If topic is a subscription object, then use that, otherwise look up the topic in
* the subscriptions object. If the topic is not found, then create a new subscription
* object and add it to the subscriptions object.
* @param {Subscription|String} topic
* @param {*} options
* @param {*} callback
* @param {*} ref
* @param {Subscription|String} topic
* @param {*} options
* @param {*} callback
* @param {*} ref
*/
node.subscribe = function (topic, options, callback, ref) {
/** @type {Subscription} */
/** @type {Subscription} */
let subscription
let doCompare = false
let changesFound = false
@@ -1004,7 +1005,7 @@ module.exports = function(RED) {
_brokerConn.unsubscribe(sub.topic, sub.ref, true)
}
})
// if subscription is found (or sent in as a parameter), then check for changes.
// if there are any changes requested, tidy up the old subscription
if (subscription) {
@@ -1091,7 +1092,7 @@ module.exports = function(RED) {
delete sub[ref]
}
}
// if instructed to remove the actual MQTT client subscription
// if instructed to remove the actual MQTT client subscription
if (unsub) {
// if there are no more subscriptions for the topic, then remove the topic
if (Object.keys(sub).length === 0) {

View File

@@ -141,15 +141,7 @@ in your Node-RED user directory (${RED.settings.userDir}).
});
}
}
/**
* @param {Object} headersObject
* @param {string} name
* @return {any} value
*/
const getHeaderValue = (headersObject, name) => {
const asLowercase = name.toLowercase();
return headersObject[Object.keys(headersObject).find(k => k.toLowerCase() === asLowercase)];
}
this.on("input",function(msg,nodeSend,nodeDone) {
checkNodeAgentPatch();
//reset redirectList on each request
@@ -300,7 +292,7 @@ in your Node-RED user directory (${RED.settings.userDir}).
}
opts.headers = {};
//add msg.headers
//add msg.headers
//NOTE: ui headers will take precidence over msg.headers
if (msg.headers) {
if (msg.headers.hasOwnProperty('x-node-red-request-node')) {
@@ -452,10 +444,6 @@ in your Node-RED user directory (${RED.settings.userDir}).
formData.append(opt, val);
} else if (typeof val === 'object' && val.hasOwnProperty('value')) {
formData.append(opt,val.value,val.options || {});
} else if (Array.isArray(val)) {
for (var i=0; i<val.length; i++) {
formData.append(opt, val[i])
}
} else {
formData.append(opt,JSON.stringify(val));
}
@@ -637,7 +625,7 @@ in your Node-RED user directory (${RED.settings.userDir}).
msg.payload = msg.payload.toString('utf8'); // txt
if (node.ret === "obj") {
if (msg.statusCode == 204){msg.payload= "{}"};
if (msg.statusCode == 204){msg.payload= "{}"};
try { msg.payload = JSON.parse(msg.payload); } // obj
catch(e) { node.warn(RED._("httpin.errors.json-error")); }
}
@@ -744,7 +732,7 @@ in your Node-RED user directory (${RED.settings.userDir}).
*
* If the algorithm directive's value ends with "-sess", then HA1 is
* HA1=digestCompute(digestCompute(username:realm:password):nonce:cnonce)
*
*
* If the algorithm directive's value does not end with "-sess", then HA1 is
* HA1=digestCompute(username:realm:password)
*/

View File

@@ -411,23 +411,33 @@ module.exports = function(RED) {
if (msg._session && msg._session.type == "tcp") {
var client = connectionPool[msg._session.id];
if (client) {
if (Buffer.isBuffer(msg.payload)) {
client.write(msg.payload);
} else if (typeof msg.payload === "string" && node.base64) {
client.write(Buffer.from(msg.payload,'base64'));
} else {
client.write(Buffer.from(""+msg.payload));
if (msg?.reset === true) {
client.destroy();
}
else {
if (Buffer.isBuffer(msg.payload)) {
client.write(msg.payload);
} else if (typeof msg.payload === "string" && node.base64) {
client.write(Buffer.from(msg.payload,'base64'));
} else {
client.write(Buffer.from(""+msg.payload));
}
}
}
}
else {
for (var i in connectionPool) {
if (Buffer.isBuffer(msg.payload)) {
connectionPool[i].write(msg.payload);
} else if (typeof msg.payload === "string" && node.base64) {
connectionPool[i].write(Buffer.from(msg.payload,'base64'));
} else {
connectionPool[i].write(Buffer.from(""+msg.payload));
if (msg?.reset === true) {
connectionPool[i].destroy();
}
else {
if (Buffer.isBuffer(msg.payload)) {
connectionPool[i].write(msg.payload);
} else if (typeof msg.payload === "string" && node.base64) {
connectionPool[i].write(Buffer.from(msg.payload,'base64'));
} else {
connectionPool[i].write(Buffer.from(""+msg.payload));
}
}
}
}
@@ -547,13 +557,33 @@ module.exports = function(RED) {
this.on("input", function(msg, nodeSend, nodeDone) {
var i = 0;
if ((!Buffer.isBuffer(msg.payload)) && (typeof msg.payload !== "string")) {
if (msg.payload !== undefined && (!Buffer.isBuffer(msg.payload)) && (typeof msg.payload !== "string")) {
msg.payload = msg.payload.toString();
}
var host = node.server || msg.host;
var port = node.port || msg.port;
if (node.out === "sit" && msg?.reset) {
if (msg.reset === true) { // kill all connections
for (var cl in clients) {
if (clients[cl].hasOwnProperty("client")) {
clients[cl].client.destroy();
delete clients[cl];
}
}
}
if (typeof(msg.reset) === "string" && msg.reset.includes(":")) { // just kill connection host:port
if (clients.hasOwnProperty(msg.reset) && clients[msg.reset].hasOwnProperty("client")) {
clients[msg.reset].client.destroy();
delete clients[msg.reset];
}
}
const cc = Object.keys(clients).length;
node.status({fill:"green",shape:cc===0?"ring":"dot",text:RED._("tcpin.status.connections",{count:cc})});
if ((host === undefined || port === undefined) && !msg.hasOwnProperty("payload")) { return; }
}
// Store client information independently
// the clients object will have:
// clients[id].client, clients[id].msg, clients[id].timeout
@@ -621,13 +651,16 @@ module.exports = function(RED) {
clients[connection_id].connecting = true;
clients[connection_id].client.connect(connOpts, function() {
//node.log(RED._("tcpin.errors.client-connected"));
node.status({fill:"green",shape:"dot",text:"common.status.connected"});
// node.status({fill:"green",shape:"dot",text:"common.status.connected"});
node.status({fill:"green",shape:"dot",text:RED._("tcpin.status.connections",{count:Object.keys(clients).length})});
if (clients[connection_id] && clients[connection_id].client) {
clients[connection_id].connected = true;
clients[connection_id].connecting = false;
let event;
while (event = dequeue(clients[connection_id].msgQueue)) {
clients[connection_id].client.write(event.msg.payload);
if (event.msg.payload !== undefined) {
clients[connection_id].client.write(event.msg.payload);
}
event.nodeDone();
}
if (node.out === "time" && node.splitc < 0) {
@@ -823,7 +856,9 @@ module.exports = function(RED) {
else if (!clients[connection_id].connecting && clients[connection_id].connected) {
if (clients[connection_id] && clients[connection_id].client) {
let event = dequeue(clients[connection_id].msgQueue)
clients[connection_id].client.write(event.msg.payload);
if (event.msg.payload !== undefined ) {
clients[connection_id].client.write(event.msg.payload);
}
event.nodeDone();
}
}

View File

@@ -17,7 +17,20 @@
</select>
<input style="width:40px;" type="text" id="node-input-sep" pattern=".">
</div>
<div class="form-row">
<label><i class="fa fa-code"></i> <span data-i18n="csv.label.spec"></span></label>
<div style="display: inline-grid;width: 70%;">
<select style="width:100%" id="csv-option-spec">
<option value="rfc" data-i18n="csv.spec.rfc"></option>
<option value="" data-i18n="csv.spec.legacy"></option>
</select>
<div>
<div class="form-tips csv-lecacy-warning" data-i18n="node-red:csv.spec.legacy_warning"
style="width: calc(100% - 18px); margin-top: 4px; max-width: unset;">
</div>
</div>
</div>
</div>
<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">
@@ -60,10 +73,10 @@
<div class="form-row" style="padding-left:20px;">
<label></label>
<label style="width:auto; margin-right:10px;" for="node-input-ret"><span data-i18n="csv.label.newline"></span></label>
<select style="width:150px;" id="node-input-ret">
<select style="width:calc(70% - 108px);" id="node-input-ret">
<option value='\r\n' data-i18n="csv.newline.windows"></option>
<option value='\n' data-i18n="csv.newline.linux"></option>
<option value='\r' data-i18n="csv.newline.mac"></option>
<option value='\r\n' data-i18n="csv.newline.windows"></option>
</select>
</div>
</script>
@@ -75,6 +88,7 @@
color:"#DEBD5C",
defaults: {
name: {value:""},
spec: {value:"rfc"},
sep: {
value:',', required:true,
label:RED._("node-red:csv.label.separator"),
@@ -83,7 +97,7 @@
hdrin: {value:""},
hdrout: {value:"none"},
multi: {value:"one",required:true},
ret: {value:'\\n'},
ret: {value:'\\r\\n'}, // default to CRLF (RFC4180 Sec 2.1: "Each record is located on a separate line, delimited by a line break (CRLF)")
temp: {value:""},
skip: {value:"0"},
strings: {value:true},
@@ -123,6 +137,27 @@
$("#node-input-sep").hide();
}
});
$("#csv-option-spec").on("change", function() {
if ($("#csv-option-spec").val() == "rfc") {
$(".form-tips.csv-lecacy-warning").hide();
} else {
$(".form-tips.csv-lecacy-warning").show();
}
});
// new nodes will have `spec` set to "rfc" (default), but existing nodes will either not have
// a spec value or it will be empty - we need to maintain the legacy behaviour for existing
// flows but default to rfc for new nodes
let spec = !this.spec ? "" : "rfc"
$("#csv-option-spec").val(spec).trigger("change")
},
oneditsave: function() {
const specFormVal = $("#csv-option-spec").val() || '' // empty === legacy
const spectNodeVal = this.spec || '' // empty === legacy, null/undefined means in-place node upgrade (keep as is)
if (specFormVal !== spectNodeVal) {
// only update the flow value if changed (avoid marking the node dirty unnecessarily)
this.spec = specFormVal
}
}
});
</script>

View File

@@ -15,319 +15,674 @@
**/
module.exports = function(RED) {
const csv = require('./lib/csv')
"use strict";
function CSVNode(n) {
RED.nodes.createNode(this,n);
this.template = (n.temp || "");
this.sep = (n.sep || ',').replace(/\\t/g,"\t").replace(/\\n/g,"\n").replace(/\\r/g,"\r");
this.quo = '"';
this.ret = (n.ret || "\n").replace(/\\n/g,"\n").replace(/\\r/g,"\r");
this.winflag = (this.ret === "\r\n");
this.lineend = "\n";
this.multi = n.multi || "one";
this.hdrin = n.hdrin || false;
this.hdrout = n.hdrout || "none";
this.goodtmpl = true;
this.skip = parseInt(n.skip || 0);
this.store = [];
this.parsestrings = n.strings;
this.include_empty_strings = n.include_empty_strings || false;
this.include_null_values = n.include_null_values || false;
if (this.parsestrings === undefined) { this.parsestrings = true; }
if (this.hdrout === false) { this.hdrout = "none"; }
if (this.hdrout === true) { this.hdrout = "all"; }
var tmpwarn = true;
var node = this;
var re = new RegExp(node.sep.replace(/[-[\]{}()*+!<=:?.\/\\^$|#\s,]/g,'\\$&') + '(?=(?:(?:[^"]*"){2})*[^"]*$)','g');
RED.nodes.createNode(this,n)
const node = this
const RFC4180Mode = n.spec === 'rfc'
const legacyMode = !RFC4180Mode
// pass in an array of column names to be trimmed, de-quoted and retrimmed
var clean = function(col,sep) {
if (sep) { re = new RegExp(sep.replace(/[-[\]{}()*+!<=:?.\/\\^$|#\s,]/g,'\\$&') +'(?=(?:(?:[^"]*"){2})*[^"]*$)','g'); }
col = col.trim().split(re) || [""];
col = col.map(x => x.replace(/"/g,'').trim());
if ((col.length === 1) && (col[0] === "")) { node.goodtmpl = false; }
else { node.goodtmpl = true; }
return col;
}
var template = clean(node.template,',');
var notemplate = template.length === 1 && template[0] === '';
node.hdrSent = false;
node.status({}) // clear status
this.on("input", function(msg, send, done) {
if (msg.hasOwnProperty("reset")) {
node.hdrSent = false;
if (legacyMode) {
this.template = (n.temp || "");
this.sep = (n.sep || ',').replace(/\\t/g,"\t").replace(/\\n/g,"\n").replace(/\\r/g,"\r");
this.quo = '"';
this.ret = (n.ret || "\n").replace(/\\n/g,"\n").replace(/\\r/g,"\r");
this.winflag = (this.ret === "\r\n");
this.lineend = "\n";
this.multi = n.multi || "one";
this.hdrin = n.hdrin || false;
this.hdrout = n.hdrout || "none";
this.goodtmpl = true;
this.skip = parseInt(n.skip || 0);
this.store = [];
this.parsestrings = n.strings;
this.include_empty_strings = n.include_empty_strings || false;
this.include_null_values = n.include_null_values || false;
if (this.parsestrings === undefined) { this.parsestrings = true; }
if (this.hdrout === false) { this.hdrout = "none"; }
if (this.hdrout === true) { this.hdrout = "all"; }
var tmpwarn = true;
// var node = this;
var re = new RegExp(node.sep.replace(/[-[\]{}()*+!<=:?.\/\\^$|#\s,]/g,'\\$&') + '(?=(?:(?:[^"]*"){2})*[^"]*$)','g');
// pass in an array of column names to be trimmed, de-quoted and retrimmed
var clean = function(col,sep) {
if (sep) { re = new RegExp(sep.replace(/[-[\]{}()*+!<=:?.\/\\^$|#\s,]/g,'\\$&') +'(?=(?:(?:[^"]*"){2})*[^"]*$)','g'); }
col = col.trim().split(re) || [""];
col = col.map(x => x.replace(/"/g,'').trim());
if ((col.length === 1) && (col[0] === "")) { node.goodtmpl = false; }
else { node.goodtmpl = true; }
return col;
}
if (msg.hasOwnProperty("payload")) {
if (typeof msg.payload == "object") { // convert object to CSV string
try {
if (!(notemplate && (msg.hasOwnProperty("parts") && msg.parts.hasOwnProperty("index") && msg.parts.index > 0))) {
template = clean(node.template);
}
var ou = "";
if (!Array.isArray(msg.payload)) { msg.payload = [ msg.payload ]; }
if (node.hdrout !== "none" && node.hdrSent === false) {
if ((template.length === 1) && (template[0] === '')) {
if (msg.hasOwnProperty("columns")) {
template = clean(msg.columns || "",",");
}
else {
template = Object.keys(msg.payload[0]);
}
var template = clean(node.template,',');
var notemplate = template.length === 1 && template[0] === '';
node.hdrSent = false;
this.on("input", function(msg, send, done) {
if (msg.hasOwnProperty("reset")) {
node.hdrSent = false;
}
if (msg.hasOwnProperty("payload")) {
if (typeof msg.payload == "object") { // convert object to CSV string
try {
if (!(notemplate && (msg.hasOwnProperty("parts") && msg.parts.hasOwnProperty("index") && msg.parts.index > 0))) {
template = clean(node.template);
}
ou += template.map(v => v.indexOf(node.sep)!==-1 ? '"'+v+'"' : v).join(node.sep) + node.ret;
if (node.hdrout === "once") { node.hdrSent = true; }
}
for (var s = 0; s < msg.payload.length; s++) {
if ((Array.isArray(msg.payload[s])) || (typeof msg.payload[s] !== "object")) {
if (typeof msg.payload[s] !== "object") { msg.payload = [ msg.payload ]; }
for (var t = 0; t < msg.payload[s].length; t++) {
if (msg.payload[s][t] === undefined) { msg.payload[s][t] = ""; }
if (msg.payload[s][t].toString().indexOf(node.quo) !== -1) { // add double quotes if any quotes
msg.payload[s][t] = msg.payload[s][t].toString().replace(/"/g, '""');
msg.payload[s][t] = node.quo + msg.payload[s][t].toString() + node.quo;
}
else if (msg.payload[s][t].toString().indexOf(node.sep) !== -1) { // add quotes if any "commas"
msg.payload[s][t] = node.quo + msg.payload[s][t].toString() + node.quo;
}
else if (msg.payload[s][t].toString().indexOf("\n") !== -1) { // add quotes if any "\n"
msg.payload[s][t] = node.quo + msg.payload[s][t].toString() + node.quo;
}
}
ou += msg.payload[s].join(node.sep) + node.ret;
}
else {
if ((template.length === 1) && (template[0] === '') && (msg.hasOwnProperty("columns"))) {
template = clean(msg.columns || "",",");
}
const ou = [];
if (!Array.isArray(msg.payload)) { msg.payload = [ msg.payload ]; }
if (node.hdrout !== "none" && node.hdrSent === false) {
if ((template.length === 1) && (template[0] === '')) {
/* istanbul ignore else */
if (tmpwarn === true) { // just warn about missing template once
node.warn(RED._("csv.errors.obj_csv"));
tmpwarn = false;
if (msg.hasOwnProperty("columns")) {
template = clean(msg.columns || "",",");
}
for (var p in msg.payload[0]) {
else {
template = Object.keys(msg.payload[0]);
}
}
ou.push(template.map(v => v.indexOf(node.sep)!==-1 ? '"'+v+'"' : v).join(node.sep));
if (node.hdrout === "once") { node.hdrSent = true; }
}
for (var s = 0; s < msg.payload.length; s++) {
if ((Array.isArray(msg.payload[s])) || (typeof msg.payload[s] !== "object")) {
if (typeof msg.payload[s] !== "object") { msg.payload = [ msg.payload ]; }
for (var t = 0; t < msg.payload[s].length; t++) {
if (msg.payload[s][t] === undefined) { msg.payload[s][t] = ""; }
if (msg.payload[s][t].toString().indexOf(node.quo) !== -1) { // add double quotes if any quotes
msg.payload[s][t] = msg.payload[s][t].toString().replace(/"/g, '""');
msg.payload[s][t] = node.quo + msg.payload[s][t].toString() + node.quo;
}
else if (msg.payload[s][t].toString().indexOf(node.sep) !== -1) { // add quotes if any "commas"
msg.payload[s][t] = node.quo + msg.payload[s][t].toString() + node.quo;
}
else if (msg.payload[s][t].toString().indexOf("\n") !== -1) { // add quotes if any "\n"
msg.payload[s][t] = node.quo + msg.payload[s][t].toString() + node.quo;
}
}
ou.push(msg.payload[s].join(node.sep));
}
else {
if ((template.length === 1) && (template[0] === '') && (msg.hasOwnProperty("columns"))) {
template = clean(msg.columns || "",",");
}
if ((template.length === 1) && (template[0] === '')) {
/* istanbul ignore else */
if (msg.payload[s].hasOwnProperty(p)) {
if (tmpwarn === true) { // just warn about missing template once
node.warn(RED._("csv.errors.obj_csv"));
tmpwarn = false;
}
const row = [];
for (var p in msg.payload[0]) {
/* istanbul ignore else */
if (typeof msg.payload[s][p] !== "object") {
// Fix to honour include null values flag
//if (typeof msg.payload[s][p] !== "object" || (node.include_null_values === true && msg.payload[s][p] === null)) {
var q = "";
if (msg.payload[s][p] !== undefined) {
q += msg.payload[s][p];
if (msg.payload[s].hasOwnProperty(p)) {
/* istanbul ignore else */
if (typeof msg.payload[s][p] !== "object") {
// Fix to honour include null values flag
//if (typeof msg.payload[s][p] !== "object" || (node.include_null_values === true && msg.payload[s][p] === null)) {
var q = "";
if (msg.payload[s][p] !== undefined) {
q += msg.payload[s][p];
}
if (q.indexOf(node.quo) !== -1) { // add double quotes if any quotes
q = q.replace(/"/g, '""');
row.push(node.quo + q + node.quo);
}
else if (q.indexOf(node.sep) !== -1 || p.indexOf("\n") !== -1) { // add quotes if any "commas" or "\n"
row.push(node.quo + q + node.quo);
}
else { row.push(q); } // otherwise just add
}
if (q.indexOf(node.quo) !== -1) { // add double quotes if any quotes
q = q.replace(/"/g, '""');
ou += node.quo + q + node.quo + node.sep;
}
else if (q.indexOf(node.sep) !== -1 || p.indexOf("\n") !== -1) { // add quotes if any "commas" or "\n"
ou += node.quo + q + node.quo + node.sep;
}
else { ou += q + node.sep; } // otherwise just add
}
}
ou.push(row.join(node.sep)); // add separator
}
ou = ou.slice(0,-1) + node.ret;
else {
const row = [];
for (var t=0; t < template.length; t++) {
if (template[t] === '') {
row.push('');
}
else {
var tt = template[t];
if (template[t].indexOf('"') >=0 ) { tt = "'"+tt+"'"; }
else { tt = '"'+tt+'"'; }
var p = RED.util.getMessageProperty(msg,'payload["'+s+'"]['+tt+']');
/* istanbul ignore else */
if (p === undefined) { p = ""; }
// fix to honour include null values flag
//if (p === null && node.include_null_values !== true) { p = "";}
p = RED.util.ensureString(p);
if (p.indexOf(node.quo) !== -1) { // add double quotes if any quotes
p = p.replace(/"/g, '""');
row.push(node.quo + p + node.quo);
}
else if (p.indexOf(node.sep) !== -1 || p.indexOf("\n") !== -1) { // add quotes if any "commas" or "\n"
row.push(node.quo + p + node.quo);
}
else { row.push(p); } // otherwise just add
}
}
ou.push(row.join(node.sep)); // add separator
}
}
}
// join lines, don't forget to add the last new line
msg.payload = ou.join(node.ret) + node.ret;
msg.columns = template.map(v => v.indexOf(',')!==-1 ? '"'+v+'"' : v).join(',');
if (msg.payload !== '') {
send(msg);
}
done();
}
catch(e) { done(e); }
}
else if (typeof msg.payload == "string") { // convert CSV string to object
try {
var f = true; // flag to indicate if inside or outside a pair of quotes true = outside.
var j = 0; // pointer into array of template items
var k = [""]; // array of data for each of the template items
var o = {}; // output object to build up
var a = []; // output array is needed for multiline option
var first = true; // is this the first line
var last = false;
var line = msg.payload;
var linecount = 0;
var tmp = "";
var has_parts = msg.hasOwnProperty("parts");
var reg = /^[-]?(?!E)(?!0\d)\d*\.?\d*(E-?\+?)?\d+$/i;
if (msg.hasOwnProperty("parts")) {
linecount = msg.parts.index;
if (msg.parts.index > node.skip) { first = false; }
if (msg.parts.hasOwnProperty("count") && (msg.parts.index+1 >= msg.parts.count)) { last = true; }
}
// For now we are just going to assume that any \r or \n means an end of line...
// got to be a weird csv that has singleton \r \n in it for another reason...
// Now process the whole file/line
var nocr = (line.match(/[\r\n]/g)||[]).length;
if (has_parts && node.multi === "mult" && nocr > 1) { tmp = ""; first = true; }
for (var i = 0; i < line.length; i++) {
if (first && (linecount < node.skip)) {
if (line[i] === "\n") { linecount += 1; }
continue;
}
if ((node.hdrin === true) && first) { // if the template is in the first line
if ((line[i] === "\n")||(line[i] === "\r")||(line.length - i === 1)) { // look for first line break
if (line.length - i === 1) { tmp += line[i]; }
template = clean(tmp,node.sep);
first = false;
}
else { tmp += line[i]; }
}
else {
for (var t=0; t < template.length; t++) {
if (template[t] === '') {
ou += node.sep;
if (line[i] === node.quo) { // if it's a quote toggle inside or outside
f = !f;
if (line[i-1] === node.quo) {
if (f === false) { k[j] += '\"'; }
} // if it's a quotequote then it's actually a quote
//if ((line[i-1] !== node.sep) && (line[i+1] !== node.sep)) { k[j] += line[i]; }
}
else if ((line[i] === node.sep) && f) { // if it is the end of the line then finish
if (!node.goodtmpl) { template[j] = "col"+(j+1); }
if ( template[j] && (template[j] !== "") ) {
// if no value between separators ('1,,"3"...') or if the line beings with separator (',1,"2"...') treat value as null
if (line[i-1] === node.sep || line[i-1].includes('\n','\r')) k[j] = null;
if ( (k[j] !== null && node.parsestrings === true) && reg.test(k[j].trim()) ) { k[j] = parseFloat(k[j].trim()); }
if (node.include_null_values && k[j] === null) o[template[j]] = k[j];
if (node.include_empty_strings && k[j] === "") o[template[j]] = k[j];
if (k[j] !== null && k[j] !== "") o[template[j]] = k[j];
}
else {
var tt = template[t];
if (template[t].indexOf('"') >=0 ) { tt = "'"+tt+"'"; }
else { tt = '"'+tt+'"'; }
var p = RED.util.getMessageProperty(msg,'payload["'+s+'"]['+tt+']');
/* istanbul ignore else */
if (p === undefined) { p = ""; }
// fix to honour include null values flag
//if (p === null && node.include_null_values !== true) { p = "";}
p = RED.util.ensureString(p);
if (p.indexOf(node.quo) !== -1) { // add double quotes if any quotes
p = p.replace(/"/g, '""');
ou += node.quo + p + node.quo + node.sep;
}
else if (p.indexOf(node.sep) !== -1 || p.indexOf("\n") !== -1) { // add quotes if any "commas" or "\n"
ou += node.quo + p + node.quo + node.sep;
}
else { ou += p + node.sep; } // otherwise just add
j += 1;
// if separator is last char in processing string line (without end of line), add null value at the end - example: '1,2,3\n3,"3",'
k[j] = line.length - 1 === i ? null : "";
}
else if (((line[i] === "\n") || (line[i] === "\r")) && f) { // handle multiple lines
//console.log(j,k,o,k[j]);
if (!node.goodtmpl) { template[j] = "col"+(j+1); }
if ( template[j] && (template[j] !== "") ) {
// if separator before end of line, set null value ie. '1,2,"3"\n1,2,\n1,2,3'
if (line[i-1] === node.sep) k[j] = null;
if ( (k[j] !== null && node.parsestrings === true) && reg.test(k[j].trim()) ) { k[j] = parseFloat(k[j].trim()); }
else { if (k[j] !== null) k[j].replace(/\r$/,''); }
if (node.include_null_values && k[j] === null) o[template[j]] = k[j];
if (node.include_empty_strings && k[j] === "") o[template[j]] = k[j];
if (k[j] !== null && k[j] !== "") o[template[j]] = k[j];
}
if (JSON.stringify(o) !== "{}") { // don't send empty objects
a.push(o); // add to the array
}
j = 0;
k = [""];
o = {};
f = true; // reset in/out flag ready for next line.
}
else { // just add to the part of the message
k[j] += line[i];
}
ou = ou.slice(0,-1) + node.ret; // remove final "comma" and add "newline"
}
}
}
msg.payload = ou;
msg.columns = template.map(v => v.indexOf(',')!==-1 ? '"'+v+'"' : v).join(',');
if (msg.payload !== '') { send(msg); }
done();
}
catch(e) { done(e); }
}
else if (typeof msg.payload == "string") { // convert CSV string to object
try {
var f = true; // flag to indicate if inside or outside a pair of quotes true = outside.
var j = 0; // pointer into array of template items
var k = [""]; // array of data for each of the template items
var o = {}; // output object to build up
var a = []; // output array is needed for multiline option
var first = true; // is this the first line
var last = false;
var line = msg.payload;
var linecount = 0;
var tmp = "";
var has_parts = msg.hasOwnProperty("parts");
var reg = /^[-]?(?!E)(?!0\d)\d*\.?\d*(E-?\+?)?\d+$/i;
if (msg.hasOwnProperty("parts")) {
linecount = msg.parts.index;
if (msg.parts.index > node.skip) { first = false; }
if (msg.parts.hasOwnProperty("count") && (msg.parts.index+1 >= msg.parts.count)) { last = true; }
}
// Finished so finalize and send anything left
if (f === false) { node.warn(RED._("csv.errors.bad_csv")); }
if (!node.goodtmpl) { template[j] = "col"+(j+1); }
// For now we are just going to assume that any \r or \n means an end of line...
// got to be a weird csv that has singleton \r \n in it for another reason...
// Now process the whole file/line
var nocr = (line.match(/[\r\n]/g)||[]).length;
if (has_parts && node.multi === "mult" && nocr > 1) { tmp = ""; first = true; }
for (var i = 0; i < line.length; i++) {
if (first && (linecount < node.skip)) {
if (line[i] === "\n") { linecount += 1; }
continue;
if ( template[j] && (template[j] !== "") ) {
if ( (k[j] !== null && node.parsestrings === true) && reg.test(k[j].trim()) ) { k[j] = parseFloat(k[j].trim()); }
else { if (k[j] !== null) k[j].replace(/\r$/,''); }
if (node.include_null_values && k[j] === null) o[template[j]] = k[j];
if (node.include_empty_strings && k[j] === "") o[template[j]] = k[j];
if (k[j] !== null && k[j] !== "") o[template[j]] = k[j];
}
if ((node.hdrin === true) && first) { // if the template is in the first line
if ((line[i] === "\n")||(line[i] === "\r")||(line.length - i === 1)) { // look for first line break
if (line.length - i === 1) { tmp += line[i]; }
template = clean(tmp,node.sep);
first = false;
}
else { tmp += line[i]; }
if (JSON.stringify(o) !== "{}") { // don't send empty objects
a.push(o); // add to the array
}
else {
if (line[i] === node.quo) { // if it's a quote toggle inside or outside
f = !f;
if (line[i-1] === node.quo) {
if (f === false) { k[j] += '\"'; }
} // if it's a quotequote then it's actually a quote
//if ((line[i-1] !== node.sep) && (line[i+1] !== node.sep)) { k[j] += line[i]; }
}
else if ((line[i] === node.sep) && f) { // if it is the end of the line then finish
if (!node.goodtmpl) { template[j] = "col"+(j+1); }
if ( template[j] && (template[j] !== "") ) {
// if no value between separators ('1,,"3"...') or if the line beings with separator (',1,"2"...') treat value as null
if (line[i-1] === node.sep || line[i-1].includes('\n','\r')) k[j] = null;
if ( (k[j] !== null && node.parsestrings === true) && reg.test(k[j].trim()) ) { k[j] = parseFloat(k[j].trim()); }
if (node.include_null_values && k[j] === null) o[template[j]] = k[j];
if (node.include_empty_strings && k[j] === "") o[template[j]] = k[j];
if (k[j] !== null && k[j] !== "") o[template[j]] = k[j];
}
j += 1;
// if separator is last char in processing string line (without end of line), add null value at the end - example: '1,2,3\n3,"3",'
k[j] = line.length - 1 === i ? null : "";
}
else if (((line[i] === "\n") || (line[i] === "\r")) && f) { // handle multiple lines
//console.log(j,k,o,k[j]);
if (!node.goodtmpl) { template[j] = "col"+(j+1); }
if ( template[j] && (template[j] !== "") ) {
// if separator before end of line, set null value ie. '1,2,"3"\n1,2,\n1,2,3'
if (line[i-1] === node.sep) k[j] = null;
if ( (k[j] !== null && node.parsestrings === true) && reg.test(k[j].trim()) ) { k[j] = parseFloat(k[j].trim()); }
else { if (k[j] !== null) k[j].replace(/\r$/,''); }
if (node.include_null_values && k[j] === null) o[template[j]] = k[j];
if (node.include_empty_strings && k[j] === "") o[template[j]] = k[j];
if (k[j] !== null && k[j] !== "") o[template[j]] = k[j];
}
if (JSON.stringify(o) !== "{}") { // don't send empty objects
a.push(o); // add to the array
}
j = 0;
k = [""];
o = {};
f = true; // reset in/out flag ready for next line.
}
else { // just add to the part of the message
k[j] += line[i];
}
}
}
// Finished so finalize and send anything left
if (f === false) { node.warn(RED._("csv.errors.bad_csv")); }
if (!node.goodtmpl) { template[j] = "col"+(j+1); }
if ( template[j] && (template[j] !== "") ) {
if ( (k[j] !== null && node.parsestrings === true) && reg.test(k[j].trim()) ) { k[j] = parseFloat(k[j].trim()); }
else { if (k[j] !== null) k[j].replace(/\r$/,''); }
if (node.include_null_values && k[j] === null) o[template[j]] = k[j];
if (node.include_empty_strings && k[j] === "") o[template[j]] = k[j];
if (k[j] !== null && k[j] !== "") o[template[j]] = k[j];
}
if (JSON.stringify(o) !== "{}") { // don't send empty objects
a.push(o); // add to the array
}
if (node.multi !== "one") {
msg.payload = a;
if (has_parts && nocr <= 1) {
if (JSON.stringify(o) !== "{}") {
node.store.push(o);
if (node.multi !== "one") {
msg.payload = a;
if (has_parts && nocr <= 1) {
if (JSON.stringify(o) !== "{}") {
node.store.push(o);
}
if (msg.parts.index + 1 === msg.parts.count) {
msg.payload = node.store;
msg.columns = template.map(v => v.indexOf(',')!==-1 ? '"'+v+'"' : v).filter(v => v).join(',');
delete msg.parts;
send(msg);
node.store = [];
}
}
if (msg.parts.index + 1 === msg.parts.count) {
msg.payload = node.store;
else {
msg.columns = template.map(v => v.indexOf(',')!==-1 ? '"'+v+'"' : v).filter(v => v).join(',');
delete msg.parts;
send(msg);
node.store = [];
send(msg); // finally send the array
}
}
else {
msg.columns = template.map(v => v.indexOf(',')!==-1 ? '"'+v+'"' : v).filter(v => v).join(',');
send(msg); // finally send the array
}
}
else {
var len = a.length;
for (var i = 0; i < len; i++) {
var newMessage = RED.util.cloneMessage(msg);
newMessage.columns = template.map(v => v.indexOf(',')!==-1 ? '"'+v+'"' : v).filter(v => v).join(',');
newMessage.payload = a[i];
if (!has_parts) {
newMessage.parts = {
id: msg._msgid,
index: i,
count: len
};
var len = a.length;
for (var i = 0; i < len; i++) {
var newMessage = RED.util.cloneMessage(msg);
newMessage.columns = template.map(v => v.indexOf(',')!==-1 ? '"'+v+'"' : v).filter(v => v).join(',');
newMessage.payload = a[i];
if (!has_parts) {
newMessage.parts = {
id: msg._msgid,
index: i,
count: len
};
}
else {
newMessage.parts.index -= node.skip;
newMessage.parts.count -= node.skip;
if (node.hdrin) { // if we removed the header line then shift the counts by 1
newMessage.parts.index -= 1;
newMessage.parts.count -= 1;
}
}
if (last) { newMessage.complete = true; }
send(newMessage);
}
else {
newMessage.parts.index -= node.skip;
newMessage.parts.count -= node.skip;
if (node.hdrin) { // if we removed the header line then shift the counts by 1
newMessage.parts.index -= 1;
newMessage.parts.count -= 1;
if (has_parts && last && len === 0) {
send({complete:true});
}
}
node.linecount = 0;
done();
}
catch(e) { done(e); }
}
else { node.warn(RED._("csv.errors.csv_js")); done(); }
}
else {
if (!msg.hasOwnProperty("reset")) {
node.send(msg); // If no payload and not reset - just pass it on.
}
done();
}
});
}
if(RFC4180Mode) {
node.template = (n.temp || "")
node.sep = (n.sep || ',').replace(/\\t/g, "\t").replace(/\\n/g, "\n").replace(/\\r/g, "\r")
node.quo = '"'
// default to CRLF (RFC4180 Sec 2.1: "Each record is located on a separate line, delimited by a line break (CRLF)")
node.ret = (n.ret || "\r\n").replace(/\\n/g, "\n").replace(/\\r/g, "\r")
node.multi = n.multi || "one"
node.hdrin = n.hdrin || false
node.hdrout = n.hdrout || "none"
node.goodtmpl = true
node.skip = parseInt(n.skip || 0)
node.store = []
node.parsestrings = n.strings
node.include_empty_strings = n.include_empty_strings || false
node.include_null_values = n.include_null_values || false
if (node.parsestrings === undefined) { node.parsestrings = true }
if (node.hdrout === false) { node.hdrout = "none" }
if (node.hdrout === true) { node.hdrout = "all" }
const dontSendHeaders = node.hdrout === "none"
const sendHeadersOnce = node.hdrout === "once"
const sendHeadersAlways = node.hdrout === "all"
const sendHeaders = !dontSendHeaders && (sendHeadersOnce || sendHeadersAlways)
const quoteables = [node.sep, node.quo, "\n", "\r"]
const templateQuoteables = [',', '"', "\n", "\r"]
let badTemplateWarnOnce = true
const columnStringToTemplateArray = function (col, sep) {
// NOTE: enforce strict column template parsing in RFC4180 mode
const parsed = csv.parse(col, { separator: sep, quote: node.quo, outputStyle: 'array', strict: true })
if (parsed.headers.length > 0) { node.goodtmpl = true } else { node.goodtmpl = false }
return parsed.headers.length ? parsed.headers : null
}
const templateArrayToColumnString = function (template, keepEmptyColumns) {
// NOTE: enforce strict column template parsing in RFC4180 mode
const parsed = csv.parse('', {headers: template, headersOnly:true, separator: ',', quote: node.quo, outputStyle: 'array', strict: true })
return keepEmptyColumns
? parsed.headers.map(e => addQuotes(e || '', { separator: ',', quoteables: templateQuoteables}))
: parsed.header // exclues empty columns
// TODO: resolve inconsistency between CSV->JSON and JSON->CSV
// CSV->JSON: empty columns are excluded
// JSON->CSV: empty columns are kept in some cases
}
function addQuotes(cell, options) {
options = options || {}
return csv.quoteCell(cell, {
quote: options.quote || node.quo || '"',
separator: options.separator || node.sep || ',',
quoteables: options.quoteables || quoteables
})
}
const hasTemplate = (t) => t?.length > 0 && !(t.length === 1 && t[0] === '')
let template
try {
template = columnStringToTemplateArray(node.template, ',') || ['']
} catch (e) {
node.warn(RED._("csv.errors.bad_template")) // is warning really necessary now we have status?
node.status({ fill: "red", shape: "dot", text: RED._("csv.errors.bad_template") })
return // dont hook up the node
}
const noTemplate = hasTemplate(template) === false
node.hdrSent = false
node.on("input", function (msg, send, done) {
node.status({}) // clear status
if (msg.hasOwnProperty("reset")) {
node.hdrSent = false
}
if (msg.hasOwnProperty("payload")) {
let inputData = msg.payload
if (typeof inputData == "object") { // convert object to CSV string
try {
// first determine the payload kind. Array or objects? Array of primitives? Array of arrays? Just an object?
// then, if necessary, convert to an array of objects/arrays
let isObject = !Array.isArray(inputData) && typeof inputData === 'object'
let isArrayOfObjects = Array.isArray(inputData) && inputData.length > 0 && typeof inputData[0] === 'object'
let isArrayOfArrays = Array.isArray(inputData) && inputData.length > 0 && Array.isArray(inputData[0])
let isArrayOfPrimitives = Array.isArray(inputData) && inputData.length > 0 && typeof inputData[0] !== 'object'
if (isObject) {
inputData = [inputData]
isArrayOfObjects = true
isObject = false
} else if (isArrayOfPrimitives) {
inputData = [inputData]
isArrayOfArrays = true
isArrayOfPrimitives = false
}
const stringBuilder = []
if (!(noTemplate && (msg.hasOwnProperty("parts") && msg.parts.hasOwnProperty("index") && msg.parts.index > 0))) {
template = columnStringToTemplateArray(node.template) || ['']
}
// build header line
if (sendHeaders && node.hdrSent === false) {
if (hasTemplate(template) === false) {
if (msg.hasOwnProperty("columns")) {
template = columnStringToTemplateArray(msg.columns || "", ",") || ['']
}
else {
template = Object.keys(inputData[0]) || ['']
}
}
if (last) { newMessage.complete = true; }
send(newMessage);
stringBuilder.push(templateArrayToColumnString(template, true))
if (sendHeadersOnce) { node.hdrSent = true }
}
if (has_parts && last && len === 0) {
send({complete:true});
// build csv lines
for (let s = 0; s < inputData.length; s++) {
let row = inputData[s]
if (isArrayOfArrays) {
/*** row is an array of arrays ***/
const _hasTemplate = hasTemplate(template)
const len = _hasTemplate ? template.length : row.length
const result = []
for (let t = 0; t < len; t++) {
let cell = row[t]
if (cell === undefined) { cell = "" }
if(_hasTemplate) {
const header = template[t]
if (header) {
result[t] = addQuotes(RED.util.ensureString(cell))
}
} else {
result[t] = addQuotes(RED.util.ensureString(cell))
}
}
stringBuilder.push(result.join(node.sep))
} else {
/*** row is an object ***/
if (hasTemplate(template) === false && (msg.hasOwnProperty("columns"))) {
template = columnStringToTemplateArray(msg.columns || "", ",")
}
if (hasTemplate(template) === false) {
/*** row is an object but we still don't have a template ***/
if (badTemplateWarnOnce === true) {
node.warn(RED._("csv.errors.obj_csv"))
badTemplateWarnOnce = false
}
const rowData = []
for (let header in inputData[0]) {
if (row.hasOwnProperty(header)) {
const cell = row[header]
if (typeof cell !== "object") {
let cellValue = ""
if (cell !== undefined) {
cellValue += cell
}
rowData.push(addQuotes(cellValue))
}
}
}
stringBuilder.push(rowData.join(node.sep))
} else {
/*** row is an object and we have a template ***/
const rowData = []
for (let t = 0; t < template.length; t++) {
if (!template[t]) {
rowData.push('')
}
else {
let cellValue = inputData[s][template[t]]
if (cellValue === undefined) { cellValue = "" }
cellValue = RED.util.ensureString(cellValue)
rowData.push(addQuotes(cellValue))
}
}
stringBuilder.push(rowData.join(node.sep)); // add separator
}
}
}
// join lines, don't forget to add the last new line
msg.payload = stringBuilder.join(node.ret) + node.ret
msg.columns = templateArrayToColumnString(template)
if (msg.payload !== '') { send(msg) }
done()
}
catch (e) {
done(e)
}
node.linecount = 0;
done();
}
catch(e) { done(e); }
else if (typeof inputData == "string") { // convert CSV string to object
try {
let firstLine = true; // is this the first line
let last = false
let linecount = 0
const has_parts = msg.hasOwnProperty("parts")
// determine if this is a multi part message and if so what part we are processing
if (msg.hasOwnProperty("parts")) {
linecount = msg.parts.index
if (msg.parts.index > node.skip) { firstLine = false }
if (msg.parts.hasOwnProperty("count") && (msg.parts.index + 1 >= msg.parts.count)) { last = true }
}
// If skip is set, compute the cursor position to start parsing from
let _cursor = 0
if (node.skip > 0 && linecount < node.skip) {
for (; _cursor < inputData.length; _cursor++) {
if (firstLine && (linecount < node.skip)) {
if (inputData[_cursor] === "\r" || inputData[_cursor] === "\n") {
linecount += 1
}
continue
}
break
}
if (_cursor >= inputData.length) {
return // skip this line
}
}
// count the number of line breaks in the string
const noofCR = ((_cursor ? inputData.slice(_cursor) : inputData).match(/[\r\n]/g) || []).length
// if we have `parts` and we are outputting multiple objects and we have more than one line
// then we need to set firstLine to true so that we process the header line
if (has_parts && node.multi === "mult" && noofCR > 1) {
firstLine = true
}
// if we are processing the first line and the node has been set to extract the header line
// update the template with the header line
if (firstLine && node.hdrin === true) {
/** @type {import('./lib/csv/index.js').CSVParseOptions} */
const csvOptionsForHeaderRow = {
cursor: _cursor,
separator: node.sep,
quote: node.quo,
dataHasHeaderRow: true,
headersOnly: true,
outputStyle: 'array',
strict: true // enforce strict parsing of the header row
}
try {
const csvHeader = csv.parse(inputData, csvOptionsForHeaderRow)
template = csvHeader.headers
_cursor = csvHeader.cursor
} catch (e) {
// node.warn(RED._("csv.errors.bad_template")) // add warning?
node.status({ fill: "red", shape: "dot", text: RED._("csv.errors.bad_template") })
throw e
}
}
// now we process the data lines
/** @type {import('./lib/csv/index.js').CSVParseOptions} */
const csvOptions = {
cursor: _cursor,
separator: node.sep,
quote: node.quo,
dataHasHeaderRow: false,
headers: hasTemplate(template) ? template : null,
outputStyle: 'object',
includeNullValues: node.include_null_values,
includeEmptyStrings: node.include_empty_strings,
parseNumeric: node.parsestrings,
strict: false // relax the strictness of the parser for data rows
}
const csvParseResult = csv.parse(inputData, csvOptions)
const data = csvParseResult.data
// output results
if (node.multi !== "one") {
if (has_parts && noofCR <= 1) {
if (data.length > 0) {
node.store.push(...data)
}
if (msg.parts.index + 1 === msg.parts.count) {
msg.payload = node.store
msg.columns = csvParseResult.header
// msg._mode = 'RFC4180 mode'
delete msg.parts
send(msg)
node.store = []
}
}
else {
msg.columns = csvParseResult.header
// msg._mode = 'RFC4180 mode'
msg.payload = data
send(msg); // finally send the array
}
}
else {
const len = data.length
for (let row = 0; row < len; row++) {
const newMessage = RED.util.cloneMessage(msg)
newMessage.columns = csvParseResult.header
newMessage.payload = data[row]
if (!has_parts) {
newMessage.parts = {
id: msg._msgid,
index: row,
count: len
}
}
else {
newMessage.parts.index -= node.skip
newMessage.parts.count -= node.skip
if (node.hdrin) { // if we removed the header line then shift the counts by 1
newMessage.parts.index -= 1
newMessage.parts.count -= 1
}
}
if (last) { newMessage.complete = true }
// newMessage._mode = 'RFC4180 mode'
send(newMessage)
}
if (has_parts && last && len === 0) {
// send({complete:true, _mode: 'RFC4180 mode'})
send({ complete: true })
}
}
node.linecount = 0
done()
}
catch (e) {
done(e)
}
}
else {
// RFC-vs-legacy mode difference: In RFC mode, we throw catchable errors and provide a status message
const err = new Error(RED._("csv.errors.csv_js"))
node.status({ fill: "red", shape: "dot", text: err.message })
done(err)
}
}
else { node.warn(RED._("csv.errors.csv_js")); done(); }
}
else {
if (!msg.hasOwnProperty("reset")) {
node.send(msg); // If no payload and not reset - just pass it on.
else {
if (!msg.hasOwnProperty("reset")) {
node.send(msg); // If no payload and not reset - just pass it on.
}
done()
}
done();
}
});
})
}
}
RED.nodes.registerType("csv",CSVNode);
RED.nodes.registerType("csv",CSVNode)
}

View File

@@ -41,8 +41,8 @@
color:"#DEBD5C",
defaults: {
name: {value:""},
property: {value:"payload"},
outproperty: {value:"payload"},
property: {value:"payload", validate: RED.validators.typedInput({ type: 'msg', allowUndefined: true }) },
outproperty: {value:"payload", validate: RED.validators.typedInput({ type: 'msg', allowUndefined: true }) },
tag: {value:""},
ret: {value:"html"},
as: {value:"single"}

View File

@@ -32,6 +32,7 @@
defaults: {
name: {value:""},
property: {value:"payload",required:true,
validate: RED.validators.typedInput({ type: 'msg', allowUndefined: true}),
label:RED._("node-red:json.label.property")},
action: {value:""},
pretty: {value:false}

View File

@@ -27,7 +27,8 @@
defaults: {
name: {value:""},
property: {value:"payload",required:true,
label:RED._("node-red:common.label.property")},
label:RED._("node-red:common.label.property"),
validate: RED.validators.typedInput({ type: 'msg', allowUndefined: true })},
attr: {value:""},
chr: {value:""}
},

View File

@@ -16,6 +16,7 @@
color:"#DEBD5C",
defaults: {
property: {value:"payload",required:true,
validate: RED.validators.typedInput({ type: 'msg', allowUndefined: true }),
label:RED._("node-red:common.label.property")},
name: {value:""}
},

View File

@@ -0,0 +1,324 @@
/**
* @typedef {Object} CSVParseOptions
* @property {number} [cursor=0] - an index into the CSV to start parsing from
* @property {string} [separator=','] - the separator character
* @property {string} [quote='"'] - the quote character
* @property {boolean} [headersOnly=false] - only parse the headers and return them
* @property {string[]} [headers=[]] - an array of headers to use instead of the first row of the CSV data
* @property {boolean} [dataHasHeaderRow=true] - whether the CSV data to parse has a header row
* @property {boolean} [outputHeader=true] - whether the output data should include a header row (only applies to array output)
* @property {boolean} [parseNumeric=false] - parse numeric values into numbers
* @property {boolean} [includeNullValues=false] - include null values in the output
* @property {boolean} [includeEmptyStrings=true] - include empty strings in the output
* @property {string} [outputStyle='object'] - output an array of arrays or an array of objects
* @property {boolean} [strict=false] - throw an error if the CSV is malformed
*/
/**
* Parses a CSV string into an array of arrays or an array of objects.
*
* NOTES:
* * Deviations from the RFC4180 spec (for the sake of user fiendliness, system implementations and flexibility), this parser will:
* * accept any separator character, not just `,`
* * accept any quote character, not just `"`
* * parse `\r`, `\n` or `\r\n` as line endings (RRFC4180 2.1 states lines are separated by CRLF)
* * Only single character `quote` is supported
* * `quote` is `"` by default
* * Any cell that contains a `quote` or `separator` will be quoted
* * Any `quote` characters inside a cell will be escaped as per RFC 4180 2.6
* * Only single character `separator` is supported
* * Only `array` and `object` output styles are supported
* * `array` output style is an array of arrays [[],[],[]]
* * `object` output style is an array of objects [{},{},{}]
* * Only `headers` or `dataHasHeaderRow` are supported, not both
* @param {string} csvIn - the CSV string to parse
* @param {CSVParseOptions} parseOptions - options
* @throws {Error}
*/
function parse(csvIn, parseOptions) {
/* Normalise options */
parseOptions = parseOptions || {};
const separator = parseOptions.separator ?? ',';
const quote = parseOptions.quote ?? '"';
const headersOnly = parseOptions.headersOnly ?? false;
const headers = Array.isArray(parseOptions.headers) ? parseOptions.headers : []
const dataHasHeaderRow = parseOptions.dataHasHeaderRow ?? true;
const outputHeader = parseOptions.outputHeader ?? true;
const parseNumeric = parseOptions.parseNumeric ?? false;
const includeNullValues = parseOptions.includeNullValues ?? false;
const includeEmptyStrings = parseOptions.includeEmptyStrings ?? true;
const outputStyle = ['array', 'object'].includes(parseOptions.outputStyle) ? parseOptions.outputStyle : 'object'; // 'array [[],[],[]]' or 'object [{},{},{}]
const strict = parseOptions.strict ?? false
/* Local variables */
const cursorMax = csvIn.length;
const ouputArrays = outputStyle === 'array';
const headersSupplied = headers.length > 0
// The original regex was an "is-a-number" positive logic test. /^ *[-]?(?!E)(?!0\d)\d*\.?\d*(E-?\+?)?\d+ *$/i;
// Below, is less strict and inverted logic but coupled with +cast it is 13%+ faster than original regex+parsefloat
// and has the benefit of understanding hexadecimals, binary and octal numbers.
const skipNumberConversion = /^ *(\+|-0\d|0\d)/
const cellBuilder = []
let rowBuilder = []
let cursor = typeof parseOptions.cursor === 'number' ? parseOptions.cursor : 0;
let newCell = true, inQuote = false, closed = false, output = [];
/* inline helper functions */
const finaliseCell = () => {
let cell = cellBuilder.join('')
cellBuilder.length = 0
// push the cell:
// NOTE: if cell is empty but newCell==true, then this cell had zero chars - push `null`
// otherwise push empty string
return rowBuilder.push(cell || (newCell ? null : ''))
}
const finaliseRow = () => {
if (cellBuilder.length) {
finaliseCell()
}
if (rowBuilder.length) {
output.push(rowBuilder)
rowBuilder = []
}
}
/* Main parsing loop */
while (cursor < cursorMax) {
const char = csvIn[cursor]
if (inQuote) {
if (char === quote && csvIn[cursor + 1] === quote) {
cellBuilder.push(quote)
cursor += 2;
newCell = false;
closed = false;
} else if (char === quote) {
inQuote = false;
cursor += 1;
newCell = false;
closed = true;
} else {
cellBuilder.push(char)
newCell = false;
closed = false;
cursor++;
}
} else {
if (char === separator) {
finaliseCell()
cursor += 1;
newCell = true;
closed = false;
} else if (char === quote) {
if (newCell) {
inQuote = true;
cursor += 1;
newCell = false;
closed = false;
}
else if (strict) {
throw new UnquotedQuoteError(cursor)
} else {
// not strict, keep 1 quote if the next char is not a cell/record separator
cursor++
if (csvIn[cursor] && csvIn[cursor] !== '\n' && csvIn[cursor] !== '\r' && csvIn[cursor] !== separator) {
cellBuilder.push(char)
if (csvIn[cursor] === quote) {
cursor++ // skip the next quote
}
}
}
} else {
if (char === '\n' || char === '\r') {
finaliseRow()
if (csvIn[cursor + 1] === '\n') {
cursor += 2;
} else {
cursor++
}
newCell = true;
closed = false;
if (headersOnly) {
break
}
} else {
if (closed) {
if (strict) {
throw new DataAfterCloseError(cursor)
} else {
cursor--; // move back to grab the previously discarded char
closed = false
}
} else {
cellBuilder.push(char)
newCell = false;
cursor++;
}
}
}
}
}
if (strict && inQuote) {
throw new ParseError(`Missing quote, unclosed cell`, cursor)
}
// finalise the last cell/row
finaliseRow()
let firstRowIsHeader = false
// if no headers supplied, generate them
if (output.length >= 1) {
if (headersSupplied) {
// headers already supplied
} else if (dataHasHeaderRow) {
// take the first row as the headers
headers.push(...output[0])
firstRowIsHeader = true
} else {
// generate headers col1, col2, col3, etc
for (let i = 0; i < output[0].length; i++) {
headers.push("col" + (i + 1))
}
}
}
const finalResult = {
/** @type {String[]} headers as an array of string */
headers: headers,
/** @type {String} headers as a comma-separated string */
header: null,
/** @type {Any[]} Result Data (may include header row: check `firstRowIsHeader` flag) */
data: [],
/** @type {Boolean|undefined} flag to indicate if the first row is a header row (only applies when `outputStyle` is 'array') */
firstRowIsHeader: undefined,
/** @type {'array'|'object'} flag to indicate the output style */
outputStyle: outputStyle,
/** @type {Number} The current cursor position */
cursor: cursor,
}
const quotedHeaders = []
for (let i = 0; i < headers.length; i++) {
if (!headers[i]) {
continue
}
quotedHeaders.push(quoteCell(headers[i], { quote, separator: ',' }))
}
finalResult.header = quotedHeaders.join(',') // always quote headers and join with comma
// output is an array of arrays [[],[],[]]
if (ouputArrays || headersOnly) {
if (!firstRowIsHeader && !headersOnly && outputHeader && headers.length > 0) {
if (output.length > 0) {
output.unshift(headers)
} else {
output = [headers]
}
firstRowIsHeader = true
}
if (headersOnly) {
delete finalResult.firstRowIsHeader
return finalResult
}
finalResult.firstRowIsHeader = firstRowIsHeader
finalResult.data = (firstRowIsHeader && !outputHeader) ? output.slice(1) : output
return finalResult
}
// output is an array of objects [{},{},{}]
const outputObjects = []
let i = firstRowIsHeader ? 1 : 0
for (; i < output.length; i++) {
const rowObject = {}
let isEmpty = true
for (let j = 0; j < headers.length; j++) {
if (!headers[j]) {
continue
}
let v = output[i][j] === undefined ? null : output[i][j]
if (v === null && !includeNullValues) {
continue
} else if (v === "" && !includeEmptyStrings) {
continue
} else if (parseNumeric === true && v && !skipNumberConversion.test(v)) {
const vTemp = +v
const isNumber = !isNaN(vTemp)
if(isNumber) {
v = vTemp
}
}
rowObject[headers[j]] = v
isEmpty = false
}
// determine if this row is empty
if (!isEmpty) {
outputObjects.push(rowObject)
}
}
finalResult.data = outputObjects
delete finalResult.firstRowIsHeader
return finalResult
}
/**
* Quotes a cell in a CSV string if necessary. Addiionally, any double quotes inside the cell will be escaped as per RFC 4180 2.6 (https://datatracker.ietf.org/doc/html/rfc4180#section-2).
* @param {string} cell - the string to quote
* @param {*} options - options
* @param {string} [options.quote='"'] - the quote character
* @param {string} [options.separator=','] - the separator character
* @param {string[]} [options.quoteables] - an array of characters that, when encountered, will trigger the application of outer quotes
* @returns
*/
function quoteCell(cell, { quote = '"', separator = ",", quoteables } = {
quote: '"',
separator: ",",
quoteables: [quote, separator, '\r', '\n']
}) {
quoteables = quoteables || [quote, separator, '\r', '\n'];
let doubleUp = false;
if (cell.indexOf(quote) !== -1) { // add double quotes if any quotes
doubleUp = true;
}
const quoteChar = quoteables.some(q => cell.includes(q)) ? quote : '';
return quoteChar + (doubleUp ? cell.replace(/"/g, '""') : cell) + quoteChar;
}
// #region Custom Error Classes
class ParseError extends Error {
/**
* @param {string} message - the error message
* @param {number} cursor - the cursor index where the error occurred
*/
constructor(message, cursor) {
super(message)
this.name = 'ParseError'
this.cursor = cursor
}
}
class UnquotedQuoteError extends ParseError {
/**
* @param {number} cursor - the cursor index where the error occurred
*/
constructor(cursor) {
super('Quote found in the middle of an unquoted field', cursor)
this.name = 'UnquotedQuoteError'
}
}
class DataAfterCloseError extends ParseError {
/**
* @param {number} cursor - the cursor index where the error occurred
*/
constructor(cursor) {
super('Data found after closing quote', cursor)
this.name = 'DataAfterCloseError'
}
}
// #endregion
exports.parse = parse
exports.quoteCell = quoteCell
exports.ParseError = ParseError
exports.UnquotedQuoteError = UnquotedQuoteError
exports.DataAfterCloseError = DataAfterCloseError

View File

@@ -15,7 +15,11 @@
-->
<script type="text/html" data-template-name="split">
<div class="form-row"><span data-i18n="[html]split.intro"></span></div>
<!-- <div class="form-row"><span data-i18n="[html]split.intro"></span></div> -->
<div class="form-row">
<label for="node-input-property"><i class="fa fa-forward"></i> <span data-i18n="split.split"></span></label>
<input type="text" id="node-input-property" style="width:70%;"/>
</div>
<div class="form-row"><span data-i18n="[html]split.strBuff"></span></div>
<div class="form-row">
<label for="node-input-splt" style="padding-left:10px; margin-right:-10px;" data-i18n="split.splitUsing"></label>
@@ -39,10 +43,9 @@
<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>
<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">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="node-red:common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]node-red:common.label.name">
</div>
</script>
@@ -57,7 +60,8 @@
arraySplt: {value:1},
arraySpltType: {value:"len"},
stream: {value:false},
addname: {value:""}
addname: {value:"", validate: RED.validators.typedInput({ type: 'msg', allowBlank: true })},
property: {value:"payload",required:true}
},
inputs:1,
outputs:1,
@@ -69,6 +73,10 @@
return this.name?"node_label_italic":"";
},
oneditprepare: function() {
if (this.property === undefined) {
$("#node-input-property").val("payload");
}
$("#node-input-property").typedInput({default:'msg',types:['msg']});
$("#node-input-splt").typedInput({
default: 'str',
typeField: $("#node-input-spltType"),
@@ -208,7 +216,22 @@
validate:RED.validators.typedInput("propertyType", false)
},
propertyType: { value:"msg"},
key: {value:"topic"},
key: {value:"topic", validate: (function () {
const typeValidator = RED.validators.typedInput({ type: 'msg' })
return function(v, opt) {
const joinMode = $("#node-input-mode").val() || this.mode
if (joinMode !== 'custom') {
return true
}
const buildType = $("#node-input-build").val() || this.build
if (buildType !== 'object') {
return true
} else {
return typeValidator(v, opt)
}
}
})()
},
joiner: { value:"\\n"},
joinerType: { value:"str"},
accumulate: { value:"false" },

View File

@@ -19,13 +19,13 @@ module.exports = function(RED) {
function sendArray(node,msg,array,send) {
for (var i = 0; i < array.length-1; i++) {
msg.payload = array[i];
RED.util.setMessageProperty(msg,node.property,array[i]);
msg.parts.index = node.c++;
if (node.stream !== true) { msg.parts.count = array.length; }
send(RED.util.cloneMessage(msg));
}
if (node.stream !== true) {
msg.payload = array[i];
RED.util.setMessageProperty(msg,node.property,array[i]);
msg.parts.index = node.c++;
msg.parts.count = array.length;
send(RED.util.cloneMessage(msg));
@@ -40,10 +40,12 @@ module.exports = function(RED) {
node.stream = n.stream;
node.spltType = n.spltType || "str";
node.addname = n.addname || "";
node.property = n.property||"payload";
try {
if (node.spltType === "str") {
this.splt = (n.splt || "\\n").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 (node.spltType === "bin") {
}
else if (node.spltType === "bin") {
var spltArray = JSON.parse(n.splt);
if (Array.isArray(spltArray)) {
this.splt = Buffer.from(spltArray);
@@ -51,7 +53,8 @@ module.exports = function(RED) {
throw new Error("not an array");
}
this.spltBuffer = spltArray;
} else if (node.spltType === "len") {
}
else if (node.spltType === "len") {
this.splt = parseInt(n.splt);
if (isNaN(this.splt) || this.splt < 1) {
throw new Error("invalid split length: "+n.splt);
@@ -69,18 +72,22 @@ module.exports = function(RED) {
node.buffer = Buffer.from([]);
node.pendingDones = [];
this.on("input", function(msg, send, done) {
if (msg.hasOwnProperty("payload")) {
var value = RED.util.getMessageProperty(msg,node.property);
if (value !== undefined) {
if (msg.hasOwnProperty("parts")) { msg.parts = { parts:msg.parts }; } // push existing parts to a stack
else { msg.parts = {}; }
msg.parts.id = RED.util.generateId(); // generate a random id
if (node.property !== "payload") {
msg.parts.property = node.property;
}
delete msg._msgid;
if (typeof msg.payload === "string") { // Split String into array
msg.payload = (node.remainder || "") + msg.payload;
if (typeof value === "string") { // Split String into array
value = (node.remainder || "") + value;
msg.parts.type = "string";
if (node.spltType === "len") {
msg.parts.ch = "";
msg.parts.len = node.splt;
var count = msg.payload.length/node.splt;
var count = value.length/node.splt;
if (Math.floor(count) !== count) {
count = Math.ceil(count);
}
@@ -89,9 +96,9 @@ module.exports = function(RED) {
node.c = 0;
}
var pos = 0;
var data = msg.payload;
var data = value;
for (var i=0; i<count-1; i++) {
msg.payload = data.substring(pos,pos+node.splt);
RED.util.setMessageProperty(msg,node.property,data.substring(pos,pos+node.splt));
msg.parts.index = node.c++;
pos += node.splt;
send(RED.util.cloneMessage(msg));
@@ -102,7 +109,7 @@ module.exports = function(RED) {
}
node.remainder = data.substring(pos);
if ((node.stream !== true) || (node.remainder.length === node.splt)) {
msg.payload = node.remainder;
RED.util.setMessageProperty(msg,node.property,node.remainder);
msg.parts.index = node.c++;
send(RED.util.cloneMessage(msg));
node.pendingDones.forEach(d => d());
@@ -119,47 +126,48 @@ module.exports = function(RED) {
if (!node.spltBufferString) {
node.spltBufferString = node.splt.toString();
}
a = msg.payload.split(node.spltBufferString);
a = value.split(node.spltBufferString);
msg.parts.ch = node.spltBuffer; // pass the split char to other end for rejoin
} else if (node.spltType === "str") {
a = msg.payload.split(node.splt);
a = value.split(node.splt);
msg.parts.ch = node.splt; // pass the split char to other end for rejoin
}
sendArray(node,msg,a,send);
done();
}
}
else if (Array.isArray(msg.payload)) { // then split array into messages
else if (Array.isArray(value)) { // then split array into messages
msg.parts.type = "array";
var count = msg.payload.length/node.arraySplt;
var count = value.length/node.arraySplt;
if (Math.floor(count) !== count) {
count = Math.ceil(count);
}
msg.parts.count = count;
var pos = 0;
var data = msg.payload;
var data = value;
msg.parts.len = node.arraySplt;
for (var i=0; i<count; i++) {
msg.payload = data.slice(pos,pos+node.arraySplt);
var m = data.slice(pos,pos+node.arraySplt);
if (node.arraySplt === 1) {
msg.payload = msg.payload[0];
m = m[0];
}
RED.util.setMessageProperty(msg,node.property,m);
msg.parts.index = i;
pos += node.arraySplt;
send(RED.util.cloneMessage(msg));
}
done();
}
else if ((typeof msg.payload === "object") && !Buffer.isBuffer(msg.payload)) {
else if ((typeof value === "object") && !Buffer.isBuffer(value)) {
var j = 0;
var l = Object.keys(msg.payload).length;
var pay = msg.payload;
var l = Object.keys(value).length;
var pay = value;
msg.parts.type = "object";
for (var p in pay) {
if (pay.hasOwnProperty(p)) {
msg.payload = pay[p];
RED.util.setMessageProperty(msg,node.property,pay[p]);
if (node.addname !== "") {
msg[node.addname] = p;
RED.util.setMessageProperty(msg,node.addname,p);
}
msg.parts.key = p;
msg.parts.index = j;
@@ -170,9 +178,9 @@ module.exports = function(RED) {
}
done();
}
else if (Buffer.isBuffer(msg.payload)) {
var len = node.buffer.length + msg.payload.length;
var buff = Buffer.concat([node.buffer, msg.payload], len);
else if (Buffer.isBuffer(value)) {
var len = node.buffer.length + value.length;
var buff = Buffer.concat([node.buffer, value], len);
msg.parts.type = "buffer";
if (node.spltType === "len") {
var count = buff.length/node.splt;
@@ -186,7 +194,7 @@ module.exports = function(RED) {
var pos = 0;
msg.parts.len = node.splt;
for (var i=0; i<count-1; i++) {
msg.payload = buff.slice(pos,pos+node.splt);
RED.util.setMessageProperty(msg,node.property,buff.slice(pos,pos+node.splt));
msg.parts.index = node.c++;
pos += node.splt;
send(RED.util.cloneMessage(msg));
@@ -197,7 +205,7 @@ module.exports = function(RED) {
}
node.buffer = buff.slice(pos);
if ((node.stream !== true) || (node.buffer.length === node.splt)) {
msg.payload = node.buffer;
RED.util.setMessageProperty(msg,node.property,node.buffer);
msg.parts.index = node.c++;
send(RED.util.cloneMessage(msg));
node.pendingDones.forEach(d => d());
@@ -230,7 +238,7 @@ module.exports = function(RED) {
var i = 0, p = 0;
pos = buff.indexOf(node.splt);
while (pos > -1) {
msg.payload = buff.slice(p,pos);
RED.util.setMessageProperty(msg,node.property,buff.slice(p,pos));
msg.parts.index = node.c++;
send(RED.util.cloneMessage(msg));
i++;
@@ -242,7 +250,7 @@ module.exports = function(RED) {
node.pendingDones = [];
}
if ((node.stream !== true) && (p < buff.length)) {
msg.payload = buff.slice(p,buff.length);
RED.util.setMessageProperty(msg,node.property,buff.slice(p,buff.length));
msg.parts.index = node.c++;
msg.parts.count = node.c++;
send(RED.util.cloneMessage(msg));
@@ -515,13 +523,13 @@ module.exports = function(RED) {
if (typeof group.joinChar !== 'string') {
groupJoinChar = group.joinChar.toString();
}
RED.util.setMessageProperty(group.msg,node.property,group.payload.join(groupJoinChar));
RED.util.setMessageProperty(group.msg,group?.prop||"payload",group.payload.join(groupJoinChar));
}
else {
if (node.propertyType === 'full') {
group.msg = RED.util.cloneMessage(group.msg);
}
RED.util.setMessageProperty(group.msg,node.property,group.payload);
RED.util.setMessageProperty(group.msg,group?.prop||"payload",group.payload);
}
if (group.msg.hasOwnProperty('parts') && group.msg.parts.hasOwnProperty('parts')) {
group.msg.parts = group.msg.parts.parts;
@@ -627,6 +635,7 @@ module.exports = function(RED) {
propertyKey = msg.parts.key;
arrayLen = msg.parts.len;
propertyIndex = msg.parts.index;
property = RED.util.getMessageProperty(msg,msg.parts.property||"payload");
}
else if (node.mode === 'reduce') {
return processReduceMessageQueue({msg, send, done});
@@ -728,6 +737,8 @@ module.exports = function(RED) {
completeSend(partId)
}, node.timer)
}
if (node.mode === "auto") { inflight[partId].prop = msg.parts.property; }
else { inflight[partId].prop = node.property; }
}
inflight[partId].dones.push(done);

View File

@@ -198,7 +198,7 @@
category: 'storage',
defaults: {
name: {value:""},
filename: {value:""},
filename: {value:"", validate: RED.validators.typedInput({ typeField: 'filenameType' })},
filenameType: {value:"str"},
appendNewline: {value:true},
createDir: {value:false},
@@ -297,7 +297,7 @@
category: 'storage',
defaults: {
name: {value:""},
filename: {value:""},
filename: {value:"", validate: RED.validators.typedInput({ typeField: 'filenameType' }) },
filenameType: {value:"str"},
format: {value:"utf8"},
chunk: {value:false},

Binary file not shown.

Before

Width:  |  Height:  |  Size: 603 B

View File

@@ -0,0 +1 @@
<svg width="40" height="60" viewBox="0, 0, 40, 60" xmlns="http://www.w3.org/2000/svg"><g fill="none" stroke="#fff"><path d="m9.7884 22.379c-5.2427-0.41732-9.6475 5.7885-7.4975 10.585 2.0949 5.2041 9.9782 6.6154 13.727 2.4477 3.633-3.5613 5.0332-9.0411 9.4821-11.853 4.5205-3.0872 11.797-0.172 12.68 5.3144 0.86 5.2537-4.8017 10.364-9.9231 8.8205-3.7873-0.85449-6.5051-4.0905-8.0487-7.4975-1.9019-3.2526-4.3882-6.7257-8.2693-7.6077-0.6891-0.15656-1.4003-0.21831-2.1059-0.21721z" stroke-width="3.3"/><path d="m6.7012 29.821h6.6154" stroke-width="1.4"/><path d="m26.988 29.821h5.5128m-2.8115-2.7564v5.5128" stroke-width="1.8"/></g></svg>

After

Width:  |  Height:  |  Size: 635 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -0,0 +1 @@
<svg width="40" height="60" viewBox="0, 0, 40, 60" xmlns="http://www.w3.org/2000/svg"><path d="m8.3474 17.75 22.298 22.444-10.747 13.013v-46.497l10.747 12.428-22.298 21.859" fill="none" stroke="#fff" stroke-width="4"/></svg>

After

Width:  |  Height:  |  Size: 225 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1 @@
<svg width="40" height="60" viewBox="0, 0, 40, 60" xmlns="http://www.w3.org/2000/svg"><path d="m2.7078 12.986c0 7.7994-0.36386 21.569 0 32.545s35.118 9.8751 34.848 0c-0.26959-9.8751 0-24.82 0-32.545 0-7.7243-34.848-7.7995-34.848 0z" fill="none" stroke="#fff"/><g fill="#fff"><path d="m3.8741 13.406v8.955c0.021834 3.5781 19.543 5.0789 25.575 3.2543 0 0 0.02229-2.6683 0.02998-2.6673l5.5325 0.7238c0.64508 0.0844 1.1345-0.74597 1.134-1.3284v-8.573l-0.99896 0.93349-15.217-2.2765c4.5883 2.1798 9.808 4.1312 9.808 4.1312-9.3667 3.1562-25.846-0.31965-25.864-3.1525z"/><path d="m3.886 26.607v8.1052c3.2188 6.1087 29.901 5.8574 32.272 0v-8.1052c-3.3598 4.6685-29.204 5.1534-32.272 0z"/><path d="m4.0032 39.082v7.1522c2.556 7.4622 28.918 7.6072 32.272 0v-7.1522c-3.2345 4.9471-29.087 5.359-32.272 0z"/></g></svg>

After

Width:  |  Height:  |  Size: 806 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 414 B

View File

@@ -0,0 +1 @@
<svg width="40" height="60" viewBox="0, 0, 40, 60" xmlns="http://www.w3.org/2000/svg"><path d="m23.515 13.831c-4.7594-5.8789-2.6084-5.7751-7.3474 0-8.0368 10.477-8.3322 24.431 2.5476 32.935 0.13181 2.0418 0.46056 4.9803 0.46056 4.9803h1.315s0.32875-2.9219 0.46017-4.9803c2.8458-2.2339 16.799-14.619 2.5641-32.935z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 335 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 671 B

View File

@@ -0,0 +1 @@
<svg width="40" height="60" viewBox="0, 0, 40, 60" xmlns="http://www.w3.org/2000/svg"><g fill="none" stroke="#fff" stroke-width="3"><path d="m6 30c6 5 24 4 29-0.07"/><path d="m21 33 0.1-19c0.02-4 4-3 4-6s-4-2-4-5"/><path d="m6 22c0-11 29-10 29 0v21c0 18-29 19-29 0s4e-7 -11 0-21z"/></g></svg>

After

Width:  |  Height:  |  Size: 293 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 252 B

View File

@@ -0,0 +1 @@
<svg width="40" height="60" viewBox="0, 0, 40, 60" xmlns="http://www.w3.org/2000/svg"><path d="m29 12s0.1 30 0.05 31-3 5-7 5-19 0.04-19 0.04c6-4 9-5 17-5 0 0 4-0.1 4-2 0-2 8e-3 -29 8e-3 -29z" fill="#fff"/><path d="m12 47s-0.1-30-0.05-31 3-5 7-5 19-0.04 19-0.04c-6 4-9 5-17 5 0 0-4 0.1-4 2 0 2-8e-3 29-8e-3 29z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 331 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 736 B

View File

@@ -0,0 +1 @@
<svg width="40" height="60" viewBox="0, 0, 40, 60" xmlns="http://www.w3.org/2000/svg"><g fill="none" stroke="#fff" stroke-width="3"><path class="cls-4" d="m17.639 30.221c-1.7087-0.88225-12.465-5.6284-14.414-6.636-1.9492-1.0075-1.9868-1.7073-0.075164-2.5188 1.9117-0.81145 12.643-5.3861 14.91-6.2738 2.2675-0.8877 3.0517-0.91493 4.9785-0.14704 1.9267 0.76789 12.026 5.1329 13.923 5.8898 1.8966 0.75699 1.9843 1.386 0.02631 2.4861-1.958 1.1001-12.1 5.6611-14.285 6.8729s-3.355 1.2091-5.0636 0.32685z"/><path class="cls-4" d="m32.23 25.251c2.8239 1.2039 4.155 1.764 4.7307 1.9938 1.8966 0.75699 1.9843 1.386 0.0263 2.4861s-12.1 5.6611-14.285 6.8729c-2.1848 1.2117-3.3548 1.209-5.0634 0.32676-1.7087-0.88225-12.465-5.6284-14.414-6.636-1.9492-1.0075-1.9868-1.7073-0.075164-2.5188 10.883-4.6196-9.1087 3.8612 4.9598-2.1076"/><path class="cls-4" d="m32.23 31.961c2.8239 1.2039 4.155 1.764 4.7307 1.9938 1.8966 0.75699 1.9843 1.386 0.0263 2.4861s-12.1 5.6611-14.285 6.8729c-2.1848 1.2117-3.3548 1.209-5.0634 0.32676-1.7087-0.88225-12.465-5.6284-14.414-6.636-1.9492-1.0075-1.9868-1.7073-0.075164-2.5188 10.883-4.6196-9.1087 3.8612 4.9598-2.1076"/></g></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -849,7 +849,13 @@
"newline": "Newline",
"usestrings": "parse numerical values",
"include_empty_strings": "include empty strings",
"include_null_values": "include null values"
"include_null_values": "include null values",
"spec": "Parser"
},
"spec": {
"rfc": "RFC4180",
"legacy": "Legacy",
"legacy_warning": "Legacy mode will be removed in a future release."
},
"placeholder": {
"columns": "comma-separated column names"
@@ -878,6 +884,7 @@
"once": "send headers once, until msg.reset"
},
"errors": {
"bad_template": "Malformed columns template.",
"csv_js": "This node only handles CSV strings or js objects.",
"obj_csv": "No columns template specified for object -> CSV.",
"bad_csv": "Malformed CSV data - output probably corrupt."
@@ -1001,7 +1008,7 @@
"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",
"intro": "Split <code>msg.payload</code> based on type:",
"object": "<b>Object</b>",
"objectSend": "Send a message for each key/value pair",

View File

@@ -30,6 +30,8 @@
before being sent.</p>
<p>If <code>msg._session</code> is not present the payload is
sent to <b>all</b> connected clients.</p>
<p>In Reply-to mode, setting <code>msg.reset = true</code> will reset the connection
specified by _session.id, or all connections if no _session.id is specified.</p>
<p><b>Note: </b>On some systems you may need root or administrator access
to access ports below 1024.</p>
</script>
@@ -40,6 +42,8 @@
returned characters into a fixed buffer, match a specified character before returning,
wait a fixed timeout from first reply and then return, sit and wait for data, or send then close the connection
immediately, without waiting for a reply.</p>
<p>If in sit and wait mode (remain connected) you can send <code>msg.reset = true</code> or <code>msg.reset = "host:port"</code> to force a break in
the connection and an automatic reconnection.</p>
<p>The response will be output in <code>msg.payload</code> as a buffer, so you may want to .toString() it.</p>
<p>If you leave tcp host or port blank they must be set by using the <code>msg.host</code> and <code>msg.port</code> properties in every message sent to the node.</p>
</script>

View File

@@ -36,7 +36,9 @@
</dl>
<h3>Details</h3>
<p>The column template can contain an ordered list of column names. When converting CSV to an object, the column names
will be used as the property names. Alternatively, the column names can be taken from the first row of the CSV.</p>
will be used as the property names. Alternatively, the column names can be taken from the first row of the CSV.
<p>When the RFC parser is selected, the column template must be compliant with RFC4180.</p>
</p>
<p>When converting to CSV, the columns template is used to identify which properties to extract from the object and in what order.</p>
<p>If the columns template is blank then you can use a simple comma separated list of properties supplied in <code>msg.columns</code> to
determine what to extract and in what order. If neither are present then all the object properties are output in the order
@@ -49,4 +51,5 @@
<p>If outputting multiple messages they will have their <code>parts</code> property set and form a complete message sequence.</p>
<p>If the node is set to only send column headers once, then setting <code>msg.reset</code> to any value will cause the node to resend the headers.</p>
<p><b>Note:</b> the column template must be comma separated - even if a different separator is chosen for the data.</p>
<p><b>Note:</b> in RFC mode, catchable errors will be thrown for malformed CSV headers and invalid input payload data</p>
</script>

View File

@@ -0,0 +1,37 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
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/html" data-help-name="inject">
<p>Inyecta un mensaje en un flujo ya sea manualmente o a intervalos regulares. La carga del mensaje puede ser de diversos tipos, incluidas cadenas, objetos JavaScript o la hora actual.</p>
<h3>Salidas</h3>
<dl class="message-properties">
<dt>payload<span class="property-type">varios</span></dt>
<dd>La carga útil configurada del mensaje.</dd>
<dt class="optional">topic <span class="property-type">texto</span></dt>
<dd>Una propiedad opcional que se puede configurar en el nodo.</dd>
</dl>
<h3>Detalles</h3>
<p>El nodo Inject puede iniciar un flujo con un valor de carga específico.
La carga predeterminada es una marca de tiempo de la hora actual en milisegundos desde el 1 de enero de 1970.</p>
<p>El nodo también admite la inyección de cadenas, números, valores booleanos, objetos JavaScript o valores de contexto global/de flujo.</p>
<p>De forma predeterminada, el nodo se activa manualmente haciendo clic en su botón dentro del editor. También se puede configurar para inyectar a intervalos regulares o según un cronograma.</p>
<p>También se puede configurar para inyectar una vez cuando se inician los flujos.</p>
<p>El <i>intervalo</i> máximo que se puede especificar es de aproximadamente 596 horas/24 días. Sin embargo, si necesitas intervalos superiores a un día, deberías considerar el uso de un nodo programador que pueda hacer frente a cortes de energía y reinicios.</p>
<p><b>Nota</b>: Las opciones <i>"Intervalo entre tiempos"</i> y <i>"en un momento específico"</i> utilizan el sistema cron estándar.
Esto significa que 20 minutos serán en la próxima hora, 20 minutos después y 40 minutos después, no dentro de 20 minutos.
Si quieres cada 20 minutos a partir de ahora, utiliza la opción <i>"intervalo"</i>.</p>
<p><b>Nota</b>: Para incluir una nueva línea en una cadena, debes usar el nodo Función o Plantilla para crear la carga.</p>
</script>

View File

@@ -0,0 +1,26 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
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/html" data-help-name="debug">
<p>Muestra las propiedades del mensaje seleccionado en la pestaña de la barra lateral de depuración y, opcionalmente, el registro de tiempo de ejecución. De forma predeterminada muestra <code>msg.payload</code>, pero se puede configurar para mostrar cualquier propiedad, el mensaje completo o el resultado de una expresión JSONata.</p>
<h3>Detalles</h3>
<p>La barra lateral de depuración proporciona una vista estructurada de los mensajes que se envían, lo que facilita la comprensión de su estructura.</p>
<p>Los objetos y matrices de JavaScript se pueden contraer y expandir según sea necesario. Los objetos del búfer se pueden mostrar como datos sin procesar o como una cadena, si es posible.</p>
<p>Junto a cada mensaje, la barra lateral de depuración incluye información sobre la hora en que se recibió el mensaje, el nodo que lo envió y el tipo de mensaje.
Al hacer clic en la identificación del nodo de origen, se mostrará ese nodo dentro del espacio de trabajo.</p>
<p>El botón del nodo se puede utilizar para habilitar o deshabilitar su salida. Se recomienda deshabilitar o eliminar cualquier nodo de depuración que no se esté utilizando.</p>
<p>El nodo también se puede configurar para enviar todos los mensajes al registro de ejecución o para enviar mensajes breves (32 caracteres) al texto de estado en el nodo de depuración.</p>
</script>

View File

@@ -0,0 +1,24 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
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/html" data-help-name="complete">
<p>Activar un flujo cuando otro nodo completa su manejo de un mensaje.</p>
<h3>Detalles</h3>
<p>Si un nodo informa cuando ha terminado de manejar un mensaje, este nodo se puede utilizar para desencadenar un segundo flujo.</p>
<p>Por ejemplo, esto se puede utilizar junto con un nodo sin puerto de salida, como el nodo de envío de correo electrónico, para continuar el flujo.</p>
<p>Este nodo debe configurarse para manejar el evento para los nodos seleccionados en el flujo. A diferencia del nodo Catch (Captura), no proporciona un modo de "manejar todo" que se aplica automáticamente a todos los nodos del flujo.</p>
<p>No todos los nodos activarán este evento; dependerá de si se han implementado para admitir esta característica tal como se introdujo en Node-RED 1.0.</p>
</script>

View File

@@ -0,0 +1,36 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
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/html" data-help-name="catch">
<p>Capturar errores arrojados por nodos en la misma pestaña.</p>
<h3>Salidas</h3>
<dl class="message-properties">
<dt>error.message <span class="property-type">texto</span></dt>
<dd>el mensaje de error.</dd>
<dt>error.source.id <span class="property-type">texto</span></dt>
<dd>la identificación del nodo que arrojó el error.</dd>
<dt>error.source.type <span class="property-type">texto</span></dt>
<dd>el tipo de nodo que arrojó el error.</dd>
<dt>error.source.name <span class="property-type">texto</span></dt>
<dd>el nombre, si está configurado, del nodo que arrojó el error.</dd>
</dl>
<h3>Detalles</h3>
<p>Si un nodo genera un error mientras maneja un mensaje, el flujo normalmente se detendrá. Este nodo se puede utilizar para detectar esos errores y manejarlos con un flujo dedicado.</p>
<p>De forma predeterminada, el nodo detectará los errores generados por cualquier nodo en la misma pestaña. Alternativamente, puede dirigirse a nodos específicos o configurarse para detectar solo errores que aún no hayan sido detectados por un nodo de captura "dirigido".</p>
<p>Cuando se produce un error, todos los nodos de captura coincidentes recibirán el mensaje.</p>
<p>Si se produce un error dentro de un subflujo, el error será manejado por cualquier nodo de captura dentro del subflujo. Si no existe ninguno, el error se propagará hasta la pestaña en la que se encuentra la instancia del subflujo.</p>
<p>Si el mensaje ya tiene una propiedad <code>error</code>, se copia a <code>_error</code>.</p>
</script>

View File

@@ -0,0 +1,34 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
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/html" data-help-name="status">
<p>Informar mensajes de estado de otros nodos en la misma pestaña.</p>
<h3>Salidas</h3>
<dl class="message-properties">
<dt>status.text <span class="property-type">texto</span></dt>
<dd>el texto de estado.</dd>
<dt>status.source.type <span class="property-type">texto</span></dt>
<dd>el tipo de nodo que informó el estado.</dd>
<dt>status.source.id <span class="property-type">texto</span></dt>
<dd>la identificación del nodo que informó el estado.</dd>
<dt>status.source.name <span class="property-type">texto</span></dt>
<dd>el nombre, si está configurado, del nodo que informó el estado.</dd>
</dl>
<h3>Detalles</h3>
<p>Este nodo no produce una <code>carga</code>.</p>
<p>De forma predeterminada, el nodo informa el estado de todos los nodos en la misma pestaña del espacio de trabajo.
Se puede configurar para informar selectivamente el estado de nodos individuales.</p>
</script>

View File

@@ -0,0 +1,53 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
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/html" data-help-name="link in">
<p>Crea cables virtuales entre flujos.</p>
<h3>Detalles</h3>
<p>El nodo se puede conectar a cualquier nodo <code>enlace salida</code> que exista en cualquier pestaña. Una vez conectados, se comportan como si estuvieran conectados entre si.</p>
<p>Los cables entre los nodos de enlace solo se muestran cuando se selecciona un nodo de enlace. Si hay cables a otras pestañas, se muestra un nodo virtual en el que se puede hacer clic para saltar a la pestaña correspondiente.</p>
<p><b>Nota: </b>No se pueden crear enlaces que entren o salgan de un subflujo.</p>
</script>
<script type="text/html" data-help-name="link out">
<p>Crea cables virtuales entre flujos.</p>
<h3>Detalles</h3>
<p>Este nodo se puede configurar para enviar mensajes a todos los nodos <code>enlace entrada</code> a los que está conectado o para enviar una respuesta al nodo <code>enlace llamada</code> que activó el flujo.</p>
<p>Cuando está en modo 'enviar a todos', los cables entre los nodos de enlace solo se muestran cuando se selecciona el nodo. Si hay cables a otras pestañas, se muestra un nodo virtual en el que se puede hacer clic para saltar a la pestaña correspondiente.</p>
<p><b>Nota: </b>No se pueden crear enlaces que entren o salgan de un subflujo.</p>
</script>
<script type="text/html" data-help-name="link call">
<p>Llama a un flujo que comienza con un enlace <code>entrada</code> y transmite la respuesta.</p>
<h3>Entradas</h3>
<dl class="message-properties">
<dt class="optional">target<span class="property-type">texto</span></dt>
<dd>Cuando la opción <b>Tipo de enlace</b> está configurada en "Destino dinámico", establece <code>msg.target</code> al nombre del nodo <code>enlace entrada</code> al que quieres llamar.</dd>
</dl>
<h3>Detalles</h3>
<p>Este nodo se puede conectar a un nodo <code>enlace entrada</code> que existe en cualquier pestaña. El flujo conectado a ese nodo debe finalizar con un nodo <code>enlace salida</code> configurado en modo 'retorno'.</p>
<p>Cuando este nodo recibe un mensaje, se pasa al nodo <code>enlace entrada</code> conectado.
Luego espera una respuesta que enviará.</p>
<p>Si no se recibe respuesta dentro del tiempo de espera configurado, predeterminado de 30 segundos, el nodo registrará un error que se puede detectar utilizando el nodo <code>captura</code>.</p>
<p>Cuando la opción <b>Tipo de enlace</b> está configurada en "Destino dinámico", code>msg.target</code> puedes usarse para realizar una llamada al nodo <code>enlace entrada</code> por nombre o ID.
<ul>
<li>Si hay un nodo <code>enlace entrada</code> con el mismo ID, se llamará</li>
<li>Si hay dos o más nodos <code>enlace entrada</code> con el mismo nombre, se generará un error</li>
<li>Un <code>enlace llamada</code> no puede llamar a un nodo <code>enlace entrada</code> dentro de un subflujo</li>
</ul>
</p>
El flujo conectado a ese nodo debe finalizar con un nodo <code>enlace salida</code> configurado en modo 'retorno'.</p>
</script>

View File

@@ -0,0 +1,21 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
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/html" data-help-name="comment">
<p>Un nodo que puedes utilizar para agregar comentarios a tus flujos.</p>
<h3>Detalles</h3>
<p>El panel de edición aceptará la sintaxis de Markdown. El texto se representará en el panel lateral de información.</p>
</script>

View File

@@ -0,0 +1,3 @@
<script type="text/html" data-help-name="global-config">
<p>Un nodo para mantener la configuración global de flujos.</p>
</script>

View File

@@ -0,0 +1,24 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
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/html" data-help-name="unknown">
<p>Este nodo es un tipo desconocido para tu instalación de Node-RED.</p>
<h3>Detalles</h3>
<p><i>Si realizas la instanciación con el nodo en este estado, se conservará tu configuración, pero el flujo no se iniciará hasta que se instales el tipo que falta.</i></p>
<p>Utiliza la opción <code>Menú - Administrar paleta</code> para buscar e instalar nodos, o <b>npm install &lt;module&gt;</b> para instalar cualquier módulo que falte, reinicia Node-RED y vuelva a importar los nodos.</p>
<p>Es posible que este tipo de nodo ya esté instalado, pero le falte una dependencia. Consulta el registro de inicio de Node-RED para ver si hay mensajes de error asociados con el tipo de nodo que falta.</p>
<p>De lo contrario, debe ponerse en contacto con el autor del flujo para obtener una copia del tipo de nodo que falta.</p>
</script>

View File

@@ -0,0 +1,55 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
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/html" data-help-name="function">
<p>Una función de JavaScript que se ejecuta en los mensajes que recibe el nodo.</p>
<p>Los mensajes se pasan como un objeto JavaScript llamado <code>msg</code>.</p>
<p>Por convención, tendrá una propiedad <code>msg.payload</code> que contiene el cuerpo del mensaje.</p>
<p>Se espera que la función devuelva un objeto de mensaje (o varios objetos de mensaje), pero puede optar por no devolver nada para detener un flujo.</p>
<p>La pestaña <b>Al iniciar</b> contiene código que se ejecutará cada vez que se inicie el nodo. La pestaña <b>Al detener</b> contiene código que se ejecutará cuando se detenga el nodo.</p>
<p>Si el código <b>Al iniciar</b> devuelve un objeto Promise, el nodo no comenzará a manejar mensajes hasta que se resuelva la promesa.</p>
<h3>Detalles</h3>
<p>Ver la <a target="_blank" href="https://nodered.org/docs/writing-functions.html">documentación online</a> para obtener más información sobre cómo escribir funciones.</p>
<h4>Enviando mensajes</h4>
<p>La función puede devolver los mensajes que quieras pasar a los siguientes nodos del flujo o puede llamar a <code>node.send(messages)</code>.</p>
<p>Puede devolver/enviar:</p>
<ul>
<li>un único objeto de mensaje - pasado a los nodos conectados a la primera salida</li>
<li>una matriz de objetos de mensaje - pasados a nodos conectados a las salidas correspondientes</li>
</ul>
<p>Nota: El código de configuración se ejecuta durante la inicialización de los nodos. Por lo tanto, si se llama a <code>node.send</code> en la pestaña de configuración, es posible que los nodos posteriores no puedan recibir el mensaje.</p>
<p>Si algún elemento de la matriz es en mismo una matriz de mensajes, se envían varios mensajes a la salida correspondiente.</p>
<p>Si se devuelve nulo, ya sea solo o como elemento de la matriz, no se transmite ningún mensaje.</p>
<h4>Registro y manejo de errores</h4>
<p>Para registrar cualquier información o informar de un error, están disponibles las siguientes funciones:</p>
<ul>
<li><code>node.log("Log message")</code></li>
<li><code>node.warn("Warning")</code></li>
<li><code>node.error("Error")</code></li>
</ul>
</p>
<p>El nodo Captura (Catch) también se puede utilizar para gestionar errores. Para invocar un nodo Catch, pasa <code>msg</code> como segundo argumento a <code>node.error</code>:</p>
<pre>node.error("Error",msg);</pre>
<h4>Accediendo a la información del nodo</h4>
<p>Las siguientes propiedades están disponibles para acceder a información sobre el nodo:</p>
<ul>
<li><code>node.id</code> - identificación del nodo</li>
<li><code>node.name</code> - nombre del nodo</li>
<li><code>node.outputCount</code> - número de salidas de nodo</li>
</ul>
<h4>Usar variables de entorno</h4>
<p>Se puede acceder a las variables de entorno utilizando <code>env.get("MY_ENV_VAR")</code>.</p>
</script>

View File

@@ -0,0 +1,37 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
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/html" data-help-name="switch">
<p>Enruta mensajes según los valores de sus propiedades o la posición de la secuencia.</p>
<h3>Detalles</h3>
<p>Cuando llega un mensaje, el nodo evaluará cada una de las reglas definidas y reenviará el mensaje a las salidas correspondientes de cualquier regla coincidente.</p>
<p>Opcionalmente, se puede configurar el nodo para que deje de evaluar reglas una vez que encuentre una que coincida.</p>
<p>Las reglas se pueden evaluar en función de una propiedad de mensaje individual, una propiedad de flujo o contexto global, una variable de entorno o el resultado de una expresión JSONata.</p>
<h4>Reglas</h4>
<p>Hay cuatro tipos de reglas.:</p>
<ol>
Las reglas <li><b>valor</b> se evalúan con respecto a la propiedad configurada</li>
Las reglas <li><b>Secuencia</b> se pueden utilizar en secuencias de mensajes, como las generadas por el nodo Dividir</li>
<li>Se puede proporcionar una <b>expresión</b> JSONata que se evaluará en relación con todo el mensaje y coincidirá si la expresión devuelve un valor verdadero.</li>
<li>Se puede utilizar una regla <b>de lo contrario</b> para hacer coincidir si ninguna de las reglas anteriores coincide.</li>
</ol>
<h4>Notas</h4>
<p>Las reglas <code>verdadero/falso</code> y <code>es nulo</code> realizan comparaciones estrictas con esos tipos. No convierten entre tipos.</p>
<p>Las reglas <code>está vacío</code> y <code>no está vacío</code> se pueden utilizar para probar la longitud de cadenas, matrices y buffers, o el número de propiedades que tiene un objeto. Ninguna regla se aprobará si la propiedad que se está probando tiene un valor <code>booleano</code>, <code>null</code> o <code>indefinido</code>.</p>
<h4>Manejo de secuencias de mensajes</h4>
<p>De forma predeterminada, el nodo no modifica la propiedad <code>msg.parts</code> de los mensajes que forman parte de una secuencia.</p>
<p>La opción <b>recrear secuencias de mensajes</b> se puede habilitar para generar nuevas secuencias de mensajes para cada regla que coincida. En este modo, el nodo almacenará en buffer toda la secuencia entrante antes de enviar las nuevas secuencias. La configuración de tiempo de ejecución <code>nodeMessageBufferMaxLength</code> se puede utilizar para limitar cuántos nodos de mensajes almacenarán en el buffer.</p>
</script>

View File

@@ -0,0 +1,33 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
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/html" data-help-name="change">
<p>Establecer, cambiar, eliminar o mover propiedades de un mensaje, contexto de flujo o contexto global.</p>
<p>El nodo puede especificar múltiples reglas que se aplicarán en el orden en que se definan.</p>
<h3>Detalles</h3>
<p>Las operaciones disponibles son:</p>
<dl class="message-properties">
<dt>Establecer</dt>
<dd>establecer una propiedad. El valor puede ser de varios tipos diferentes o puede tomarse de un mensaje existente o de una propiedad de contexto.</dd>
<dt>Cambiar</dt>
<dd>buscar y reemplazar partes de la propiedad. Si las expresiones regulares están habilitadas, la propiedad "reemplazar con" puede incluir grupos de captura, por ejemplo <code>$1</code>. Reemplazar solo cambiará el tipo si hay una coincidencia completa.</dd>
<dt>Eliminar</dt>
<dd>eliminar una propiedad.</dd>
<dt>Mover</dt>
<dd>mover o cambiar el nombre de una propiedad.</dd>
</dl>
<p>El tipo "expresión" utiliza el lenguaje de consulta y expresión <a href="http://jsonata.org/" target="_new">JSONata</a>.</p>
</script>

View File

@@ -0,0 +1,42 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
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/html" data-help-name="range">
<p>Asigna un valor numérico a un rango diferente.</p>
<h3>Entradas</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">número</span></dt>
<dd>La carga <i>debe</i> ser un número. Cualquier otra cosa intentará analizarse como un número y rechazarse si falla.</dd>
</dl>
<h3>Salidas</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">número</span></dt>
<dd>El valor asignado al nuevo rango.</dd>
</dl>
<h3>Detalles</h3>
<p>Este nodo escalará linealmente el valor recibido. De forma predeterminada, el resultado no está restringido al rango definido en el nodo.</p>
<p><i>Escalar y limitar al rango objetivo</i> significa que el resultado nunca estará fuera del rango especificado dentro del rango objetivo.</p>
<p><i>Escalar y ajustar dentro del rango objetivo</i> significa que el resultado se ajustará dentro del rango objetivo.</p>
<p><i>Escalar, pero eliminar si está fuera del rango de entrada</i> significa que el resultado se escalará, pero cualquier entrada fuera del rango de entrada y salida se eliminará.</p>
<p>Por ejemplo, una entrada 0 - 10 asignada a 0 - 100.</p>
<table style="outline-width:#888 solid thin">
<tr><th width="80px">modo</th><th width="80px">entrada</th><th width="80px">salida</th></tr>
<tr><td><center>scale</center></td><td><center>12</center></td><td><center>120</center></td></tr>
<tr><td><center>limit</center></td><td><center>12</center></td><td><center>100</center></td></tr>
<tr><td><center>wrap</center></td><td><center>12</center></td><td><center>20</center></td></tr>
<tr><td><center>drop</center></td><td><center>12</center></td><td><center><i>(sin salida)</i></center></td></tr>
</table>
</script>

View File

@@ -0,0 +1,51 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
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/html" data-help-name="template">
<p>Establece una propiedad basada en la plantilla proporcionada.</p>
<h3>Entradas</h3>
<dl class="message-properties">
<dt>msg <span class="property-type">objeto</span></dt>
<dd>Un objeto de mensaje que contiene información para completar la plantilla.</dd>
<dt class="optional">template <span class="property-type">texto</span></dt>
<dd>Una plantilla que se completará desde <code>msg.payload</code>. Si no está configurado en el panel de edición, esto se puede configurar como una propiedad de msg.</dd>
</dl>
<h3>Salidas</h3>
<dl class="message-properties">
<dt>msg <span class="property-type">objeto</span></dt>
<dd>un mensaje con una propiedad establecida al completar la plantilla configurada con propiedades del mensaje entrante.</dd>
</dl>
<h3>Detalles</h3>
<p>De forma predeterminada, esto utiliza el formato <i><a href="http://mustache.github.io/mustache.5.html" target="_blank">mustache</a></i>, pero se puede desactivar si es necesario.</p>
<p>Por ejemplo, cuando una plantilla de:
<pre>Hola {{payload.name}}. Hoy es {{date}}</pre>
<p>recibe un mensaje que contiene:
<pre>{
date: "lunes",
payload: {
name: "Fred"
}
}</pre>
<p>La propiedad resultante será:
<pre>Hola Fred. Hoy es lunes</pre>
<p>Es posible utilizar una propiedad del contexto de flujo o del contexto global. Simplemente usa <code>{{flow.name}}</code> o <code>{{global.name}}</code>, o para el almacén persistente <code>store</code> usa <code>{{ flow[store].name}}</code> o <code>{{global[store].name}}</code>.
<p><b>Nota: </b>De forma predeterminada, <i>mustache</i> codificará cualquier entidad HTML o no alfanumérica en los valores que sustituye. Para evitar esto, utilice llaves <code>{{{triple}}}</code>.</p>
<p>Si necesita utilizar <code>{{ }}</code> en su contenido, puede cambiar los caracteres utilizados para marcar las secciones con plantilla. Por ejemplo, para usar <code>[[ ]]</code> en su lugar, agregue la siguiente línea en la parte superior de la plantilla:</p>
<pre>{{=[[ ]]=}}</pre>
<h4>Usando variables de entorno</h4>
<p>El nodo de plantilla puede acceder a variables de entorno utilizando la sintaxis:</p>
<pre>Mi color favorito es {{env.COLOUR}}.</pre>
</script>

View File

@@ -0,0 +1,39 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
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/html" data-help-name="delay">
<p>Retrasa cada mensaje que pasa por el nodo o limita la velocidad a la que pueden pasar.</p>
<h3>Entradas</h3>
<dl class="message-properties">
<dt class="optional">delay <span class="property-type">número</span></dt>
<dd>Establece el retraso, en milisegundos, que se aplicará al mensaje. Esta opción solo se aplica si el nodo está configurado para permitir que el mensaje anule el intervalo de retardo predeterminado configurado.</dd>
<dt class="optional">rate <span class="property-type">número</span></dt>
<dd>Establece el valor de la tasa en milisegundos entre mensajes. Esta propiedad sobrescribe el valor de tasa existente definido en la configuración del nodo cuando recibe el mensaje que contiene el valor <code>msg.rate</code> en milisegundos. Esta opción solo se aplica si el nodo está configurado para permitir que el mensaje anule el intervalo de velocidad predeterminado configurado.</dd>
<dt class="optional">reset</dt>
<dd>Si el mensaje recibido tiene esta propiedad establecida en cualquier valor, todos los mensajes pendientes retenidos por el nodo se borran sin enviarse.</dd>
<dt class="optional">flush</dt>
<dd>Si el mensaje recibido tiene esta propiedad establecida en un valor numérico, esa cantidad de mensajes se publicará inmediatamente. Si se establece en cualquier otro tipo (por ejemplo, booleano), todos los mensajes pendientes retenidos por el nodo se envían inmediatamente.</dd>
<dt class="optional">toFront</dt>
<dd>Cuando está en modo de límite de velocidad, si el mensaje recibido tiene esta propiedad establecida en booleano <code>verdadero</code>, entonces el mensaje se envía al frente de la cola y se publicará a continuación. Esto se puede utilizar en combinación con <code>msg.flush=1</code> para reenviar inmediatamente.
</dd>
</dl>
<h3>Detalles</h3>
<p>Cuando se configura para retrasar mensajes, el intervalo de retraso puede ser un valor fijo, un valor aleatorio dentro de un rango o establecerse dinámicamente para cada mensaje. Cada mensaje se retrasa independientemente de cualquier otro mensaje, según la hora de su llegada.</p>
<p>Cuando se configura para calificar los mensajes con límite, su entrega se distribuye durante el período de tiempo configurado. El estado muestra la cantidad de mensajes actualmente en la cola. Opcionalmente, puede descartar mensajes intermedios a medida que llegan.</p>
<p>Si se configura para permitir modificar la frecuencia, la nueva tasa se aplicará inmediatamente y permanecerá vigente hasta que se cambie nuevamente, se restablezca el nodo o se reinicie el flujo.</p>
<p>La limitación de velocidad se puede aplicar a todos los mensajes o agruparlos según su valor <code>msg.topic</code>. Al agrupar, los mensajes intermedios se eliminan automáticamente. En cada intervalo de tiempo, el nodo puede publicar el mensaje más reciente para todos los temas o publicar el mensaje más reciente para el siguiente tema.</p>
<p><b>Nota</b>: En el modo de límite de velocidad, la profundidad máxima de la cola se puede establecer mediante una propiedad en su archivo <i>settings.js</i>. Por ejemplo <code>nodeMessageBufferMaxLength: 1000,</code></p>
</script>

View File

@@ -0,0 +1,37 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
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/html" data-help-name="trigger">
<p>Cuando se activa, puede enviar un mensaje y luego, opcionalmente, un segundo mensaje, a menos que se extienda o se restablezca.</p>
<h3>Entradas</h3>
<dl class="message-properties">
<dt class="optional">delay <span class="property-type">número</span></dt>
<dd>Establece el retraso, en milisegundos, que se aplicará al mensaje. Esta opción solo se aplica si el nodo está configurado para permitir que el mensaje anule el intervalo de retardo predeterminado configurado.</dd>
<dt class="optional">reset</dt>
<dd>Si se recibe un mensaje con esta propiedad, se borrará cualquier tiempo de espera o repetición actualmente en curso y no se activará ningún mensaje.</dd>
</dl>
<h3>Detalles</h3>
<p>Este nodo se puede utilizar para crear un tiempo de espera dentro de un flujo. De forma predeterminada, cuando recibe un mensaje, envía un mensaje con una <code>carga</code> de <code>1</code>. Luego espera 250 ms antes de enviar un segundo mensaje con una <code>carga</code> de <code>0</code>. Esto podría usarse, por ejemplo, para hacer parpadear un LED conectado a un pin GPIO de Raspberry Pi.</p>
<p>Las cargas de cada mensaje enviado se pueden configurar con una variedad de valores, incluida la opción de no enviar nada. Por ejemplo, configurando el mensaje inicial en <i>nada</i> y seleccionando la opción de extender el temporizador con cada mensaje recibido, el nodo actuará como un temporizador de vigilancia; solo enviar un mensaje si no se recibe nada dentro del intervalo establecido.</p>
<p>Si se establece en un tipo <i>cadena</i>, el nodo admite la sintaxis de plantilla mustache.</p>
<p>El retraso entre el envío de mensajes puede ser anulado por <code>msg.delay</code> si esa opción está habilitada en el nodo. El valor debe proporcionarse en milisegundos.</p>
<p>Si el nodo recibe un mensaje con una propiedad <code>reset</code> o una <code>carga</code> que coincide con la configurada en el nodo, cualquier tiempo de espera o repetición actualmente en curso se borrará y no se activa ningún mensaje.</p>
<p>El nodo se puede configurar para reenviar un mensaje a intervalos regulares hasta que se restablezca mediante un mensaje recibido.</p>
<p>Opcionalmente, el nodo se puede configurar para tratar los mensajes como si fueran secuencias separadas, utilizando una propiedad msg para identificar cada secuencia. <code>msg.topic</code> predeterminado.</p>
<p>El estado indica que el nodo está actualmente activo. Si se utilizan varias transmisiones, el estado indica la cantidad de transmisiones que se están realizando.</p>
</script>

View File

@@ -0,0 +1,75 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
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/html" data-help-name="exec">
<p>Ejecuta un comando del sistema y devuelve su salida.</p>
<p>El nodo se puede configurar para esperar hasta que se complete el comando o para enviar su salida a medida que el comando lo genera.</p>
<p>El comando que se ejecuta puede configurarse en el nodo o proporcionarse mediante el mensaje recibido.</p>
<h3>Entradas</h3>
<dl class="message-properties">
<dt class="optional">payload <span class="property-type">texto</span></dt>
<dd>si está configurado para hacerlo, se agregará al comando ejecutado.</dd>
<dt class="optional">kill <span class="property-type">texto</span></dt>
<dd>el tipo de señal de interrupción para enviar al proceso de ejecución existente.</dd>
<dt class="optional">pid <span class="property-type">número|texto</span></dt>
<dd>el ID de proceso del proceso de ejecución existente que se va a eliminar.</dd>
</dl>
<h3>Salidas</h3>
<ol class="node-ports">
<li>Salida estándar
<dl class="message-properties">
<dt>payload <span class="property-type">texto</span></dt>
<dd>la salida estándar del comando.</dd>
</dl>
<dl class="message-properties">
<dt>rc <span class="property-type">objeto</span></dt>
<dd>solo en modo ejecución, una copia del objeto de código de retorno (también disponible en el puerto 3)</dd>
</dl>
</li>
<li>Salida error
<dl class="message-properties">
<dt>payload <span class="property-type">texto</span></dt>
<dd>el error estándar del comando.</dd>
</dl>
<dl class="message-properties">
<dt>rc <span class="property-type">objeto</span></dt>
<dd>solo en modo ejecución, una copia del objeto de código de retorno (también disponible en el puerto 3)</dd>
</dl>
</li>
<li>Código de retorno
<dl class="message-properties">
<dt>payload <span class="property-type">objeto</span></dt>
<dd>un objeto que contiene el código de retorno y posiblemente las propiedades <code>message</code>, <code>signal</code>.</dd>
</dl>
</li>
</ol>
<h3>Detalles</h3>
<p>De forma predeterminada, utiliza la llamada al sistema <code>exec</code> que llama al comando, espera a que se complete y luego devuelve el resultado. Por ejemplo, un comando exitoso debe tener un código de retorno de <code>{ code: 0 }</code>.</p>
<p>Opcionalmente, puedes usar <code>spawn</code> en su lugar, que devuelve la salida de stdout y stderr a medida que se ejecuta el comando, generalmente una línea a la vez. Al finalizar, devuelve un objeto en el tercer puerto. Por ejemplo, un comando exitoso debería devolver <code>{ code: 0 }</code>.</p>
<p>Los errores pueden devolver información adicional en el tercer puerto <code>msg.payload</code>, como una cadena <code>message</code>, <code>señal</code>.</p>
<p>El comando que se ejecuta se define dentro del nodo, con una opción para agregar <code>msg.payload</code> y un conjunto adicional de parámetros.</p>
<p>Los comandos o parámetros con espacios deben estar entre comillas - <code>"Este es un solo parámetro"</code></p>
<p>La <code>carga</code> devuelta suele ser una <i>cadena</i>, a menos que se detecten caracteres que no sean UTF8, en cuyo caso es un <i>búfer</i>.</p>
<p>El icono de estado del nodo y el PID serán visibles mientras el nodo esté activo. Los cambios a esto pueden ser leídos por el nodo <code>Estado</code>.</p>
<p>La opción <code>Ocultar consola</code> ocultará la consola de procesos que normalmente se muestra en los sistemas Windows.</p>
<h4>Eliminando Procesos</h4>
<p>Enviar <code>msg.kill</code> eliminará un único proceso activo. <code>msg.kill</code> debe ser una cadena que contenga el tipo de señal que se enviará, por ejemplo, <code>SIGINT</code>, <code>SIGQUIT</code> o <code>SIGHUP</code>.
El valor predeterminado es <code>SIGTERM</code> si se establece en una cadena vacía.</p>
<p>Si el nodo tiene más de un proceso en ejecución, entonces <code>msg.pid</code> también debe configurarse con el valor del PID que se va a eliminar.</p>
<p>Si se proporciona un valor en el campo <code>Timeout</code>, si el proceso no se ha completado cuando haya transcurrido el número de segundos especificado, el proceso se finalizará automáticamente</p>
<p>Consejo: si ejecutas una aplicación Python, es posible que necesites usar el parámetro <code>-u</code> para detener la salida que se almacena en el búfer.</p>
</script>

View File

@@ -0,0 +1,32 @@
<script type="text/html" data-help-name="rbe">
<p>Nodo Informe por excepción (RBE): solo transmite datos si la carga ha cambiado.
También puede bloquear o ignorar si el valor cambia en una cantidad específica (modo de banda muerta y de banda estrecha).</p>
<h3>Entradas</h3>
<dl class="message-properties">
<dt>payload
<span class="property-type">número | texto | (objeto)</span>
</dt>
<dd>El modo RBE aceptará números, cadenas y objetos simples.
Otros modos deben proporcionar un número analizable.</dd>
<dt class="optional">topic <span class="property-type">texto</span>
</dt>
<dd>Si se especifica, funcionará por tema. Esta propiedad se puede establecer mediante configuración.</dd>
<dt class="optional">reset<span class="property-type">cualquiera</span></dt>
<dd>si está configurado, borra el valor almacenado para el msg.topic especificado, o todos los temas si no se especifica msg.topic.</dd>
</dl>
<h3>Salidas</h3>
<dl class="message-properties">
<dt>carga <span class="property-type">según la entrada</span></dt>
<dd>Si se activa, la salida será la misma que la entrada.</dd>
</dl>
<h3>Detalles</h3>
<p>En modo RBE, este nodo se bloqueará hasta que el valor de <code>msg.payload</code> (o propiedad seleccionada) sea diferente al anterior.
Si es necesario, puede ignorar el valor inicial para no enviar nada al inicio.</p>
<p>El modo <a href="https://en.wikipedia.org/wiki/Deadband" target="_blank">Deadband</a> bloqueará el valor entrante <i>hasta</i> que el cambio sea mayor o igual que &plusmn; la banda dada.</p>
<p>El modo de banda estrecha bloqueará el valor entrante, <i>si</i> su cambio es mayor o igual que &plusmn; la banda dada.
Es útil para ignorar valores atípicos de un sensor defectuoso, por ejemplo.</p>
<p>Tanto en el modo Banda Muerta como en el Modo Banda Estrecha, el valor entrante debe contener un número analizable y ambos también admiten %: solo se envía si/a menos que la entrada difiera en más del x% del valor original.</p>
<p>Tanto la banda muerta como la banda estrecha permiten la comparación con el valor de salida válido anterior, ignorando así cualquier valor fuera de rango, o con el valor de entrada anterior, que restablece el punto de ajuste, permitiendo así una deriva gradual (banda muerta) o un cambio en pasos (banda estrecha).</p>
<p><b>Nota:</b> Esto funciona por <code>msg.topic</code>, aunque se puede cambiar a otra propiedad si se desea.
Esto significa que un único nodo de filtro puede manejar varios temas diferentes al mismo tiempo.</p>
</script>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,19 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
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/html" data-help-name="tls-config">
<p>Opciones de configuración para conexiones TLS.</p>
</script>

View File

@@ -0,0 +1,22 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
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/html" data-help-name="http proxy">
<p>Opciones de configuración para el proxy HTTP.</p>
<h3>Detalles</h3>
<p>Al acceder un host en la lista de hosts ignorados, no se utilizará ningún proxy.</p>
</script>

View File

@@ -0,0 +1,135 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
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/html" data-help-name="mqtt in">
<p>Conecta a un gestor MQTT y se suscribe a mensajes del tema especificado.</p>
<h3>Salidas</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">texto | buffer</span></dt>
<dd>una cadena a menos que se detecte como un búfer binario.</dd>
<dt>topic <span class="property-type">texto</span></dt>
<dd>el tema MQTT, utiliza / como separador de jerarquía.</dd>
<dt>qos <span class="property-type">número</span> </dt>
<dd>0, dispara y olvida - 1, al menos una vez - 2, una vez y sólo una vez.</dd>
<dt>retain <span class="property-type">booleano</span></dt>
<dd>verdadero indica que el mensaje se retuvo y puede ser antiguo.</dd>
<dt class="optional">responseTopic <span class="property-type">texto</span></dt>
<dd><b>MQTTv5</b>: el tema de respuesta MQTT para el mensaje</dd>
<dt class="optional">correlationData <span class="property-type">Buffer</span></dt>
<dd><b>MQTTv5</b>: los datos de correlación para el mensaje</dd>
<dt class="optional">contentType <span class="property-type">texto</span></dt>
<dd><b>MQTTv5</b>: el tipo de contenido de la carga</dd>
<dt class="optional">userProperties <span class="property-type">objeto</span></dt>
<dd><b>MQTTv5</b>: cualquier propiedad de usuario del mensaje</dd>
<dt class="optional">messageExpiryInterval <span class="property-type">número</span></dt>
<dd><b>MQTTv5</b>: el tiempo de expiración, en segundos, del mensaje</dd>
</dl>
<h3>Detalles</h3>
<p>El tema de suscripción puede incluir comodines MQTT, + para un nivel, # para múltiples niveles.</p>
<p>Este nodo requiere una conexión a un gestor MQTT para configurarse. Esto se configura haciendo clic en el icono del lápiz.</p>
<p>Varios nodos MQTT (dentro o fuera) pueden compartir la misma conexión de gestor si es necesario.</p>
<h4>Suscripción Dinámica</h4>
El nodo se puede configurar para controlar dinámicamente la conexión MQTT y sus suscripciones. Cuando esté habilitado, el nodo tendrá una entrada y podrá controlarse pasándole mensajes.
<h3>Entradas</h3>
<p>Estos solo se aplican cuando el nodo ha sido configurado para suscripciones dinámicas.</p>
<dl class="message-properties">
<dt>action <span class="property-type">texto</span></dt>
<dd>el nombre de la acción que debe realizar el nodo. Las acciones disponibles son: <code>"connect"</code>, <code>"disconnect"</code>, <code>"subscribe"</code> y <code>"unsubscribe"</code>.</dd>
<dt class="optional">topic <span class="property-type">texto|objeto|matriz</span></dt>
<dd>Para las acciones <code>"subscribe"</code> y <code>"unsubscribe"</code>, esta propiedad proporciona el tema. Se puede configurar como:<ul>
<li>una cadena que contiene el filtro de tema</li>
<li>un objeto que contiene las propiedades <code>topic</code> y <code>qos</code></li>
<li>una matriz de cadenas u objetos para manejar múltiples temas en uno</li>
</ul>
</dd>
<dt class="optional">broker <span class="property-type">gestor</span> </dt>
<dd>Para la acción <code>"connect"</code>, esta propiedad puede anular cualquiera de las configuraciones de gestor individuales, incluyendo: <ul>
<li><code>broker</code></li>
<li><code>port</code></li>
<li><code>url</code> - anula el gestor/puerto para proporcionar una URL de conexión completa</li>
<li><code>username</code></li>
<li><code>password</code></li>
</ul>
<p>Si esta propiedad está configurada y el intermediario ya está conectado, se registrará un error a menos que tenga establecida la propiedad <code>force</code>; en cuyo caso se desconectará del intermediario, aplicará la nueva configuración y se volverá a conectar.</p>
</dd>
</dl>
</script>
<script type="text/html" data-help-name="mqtt out">
<p>Conecta a un gestor MQTT y publica mensajes.</p>
<h3>Entradas</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">texto | buffer</span></dt>
<dd> la carga a publicar. Si esta propiedad no está configurada, no se enviará ningún mensaje. Para enviar un mensaje en blanco, establece esta propiedad a una cadena vacía.</dd>
<dt class="optional">topic <span class="property-type">texto</span></dt>
<dd>el tema MQTT para publicar.</dd>
<dt class="optional">qos <span class="property-type">número</span></dt>
<dd>0, dispara y olvida - 1, al menos una vez - 2, una vez y sólo una vez. Predeterminado 0.</dd>
<dt class="optional">retain <span class="property-type">booleano</span></dt>
<dd>configúralo en verdadero para retener el mensaje en el gestor. Por defecto es falso.</dd>
<dt class="optional">responseTopic <span class="property-type">texto</span></dt>
<dd><b>MQTTv5</b>: el tema de respuesta MQTT para el mensaje</dd>
<dt class="optional">correlationData <span class="property-type">Buffer</span></dt>
<dd><b>MQTTv5</b>: los datos de correlación para el mensaje</dd>
<dt class="optional">contentType <span class="property-type">texto</span></dt>
<dd><b>MQTTv5</b>: el tipo de contenido de la carga</dd>
<dt class="optional">userProperties <span class="property-type">objeto</span></dt>
<dd><b>MQTTv5</b>: cualquier propiedad de usuario del mensaje</dd>
<dt class="optional">messageExpiryInterval <span class="property-type">número</span></dt>
<dd><b>MQTTv5</b>: el tiempo de expiración, en segundos, del mensaje</dd>
<dt class="optional">topicAlias <span class="property-type">número</span></dt>
<dd><b>MQTTv5</b>: el alias del tema MQTT a utilizar</dd>
</dl>
<h3>Detalles</h3>
<code>msg.payload</code> se utiliza como carga útil del mensaje publicado.
Si contiene un Objeto, se convertirá en una cadena JSON antes de enviarse.
Si contiene un búfer binario, el mensaje se publicará tal cual.</p>
<p>El tema utilizado se puede configurar en el nodo o, si se deja en blanco, se puede establecer mediante <code>msg.topic</code>.</p>
<p>Del mismo modo, los valores de QoS y retención se pueden configurar en el nodo o, si se dejan en blanco, establecer mediante <code>msg.qos</code> y <code>msg.retain</code> respectivamente. Para borrar un tema previamente retenido del gestor, envía un mensaje en blanco a ese tema con el indicador de retención configurado.</p>
<p>Este nodo requiere una conexión a un gestor MQTT para configurarse. Esto se configura haciendo clic en el icono del lápiz.</p>
<p>Varios nodos MQTT (dentro o fuera) pueden compartir la misma conexión de gestor si es necesario.</p>
<h4>Control Dinámico</h4>
La conexión compartida por el nodo se puede controlar dinámicamente. Si el nodo recibe uno de los siguientes mensajes de control, tampoco publicará la carga del mensaje.
<h3>Entradas</h3>
<dl class="message-properties">
<dt>action <span class="property-type">texto</span></dt>
<dd>el nombre de la acción que debe realizar el nodo. Las acciones disponibles son: <code>"connect"</code> y <code>"disconnect"</code>.</dd>
<dt class="optional">broker <span class="property-type">gestor</span> </dt>
<dd>Para la acción <code>"connect"</code>, esta propiedad puede anular cualquiera de las configuraciones de gestor individuales, incluyendo: <ul>
<li><code>broker</code></li>
<li><code>port</code></li>
<li><code>url</code> - anula el gestor/puerto para proporcionar una URL de conexión completa</li>
<li><code>username</code></li>
<li><code>password</code></li>
</ul>
<p>Si esta propiedad está configurada y el gestor ya está conectado, se registrará un error a menos que tenga establecida la propiedad <code>force</code>; en cuyo caso se desconectará del gestor, aplicará la nueva configuración y se volverá a conectar.</p>
</dd>
</dl>
</script>
<script type="text/html" data-help-name="mqtt-broker">
<p>Configuración para una conexión a un gestor MQTT.</p>
<p>Esta configuración creará una conexión única con el gestor que luego podrá ser reutilizada por los nodos <code>MQTT In</code> y <code>MQTT Out</code>.</p>
<p>El nodo generará una ID de cliente aleatoria si no se establece ninguna y el nodo está configurado para utilizar una conexión de sesión limpia. Si se establece un ID de cliente, debe ser exclusivo del corredor al que se está conectando.</p>
<h4>Mensaje Inicial</h4>
<p>Este es un mensaje que se publicará en el tema configurado cada vez que se establezca la conexión.</p>
<h4>Mensaje Cierre</h4>
<p>Este es un mensaje que se publicará en el tema configurado antes de que la conexión se cierre normalmente, ya sea volviendo a implementar el nodo o apagándolo.</p>
<h4>Mensaje Voluntad</h4>
<p>Este es un mensaje que será publicado por el gestor en caso de que el nodo pierda inesperadamente su conexión.</p>
<h4>WebSockets</h4>
<p>El nodo se puede configurar para utilizar una conexión WebSocket. Para hacerlo, el campo Servidor debe configurarse con un URI completo para la conexión. Por ejemplo:</p>
<pre>ws://example.com:4000/mqtt</pre>
</script>

View File

@@ -0,0 +1,83 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
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/html" data-help-name="http in">
<p>Crea un punto final HTTP para crear servicios web.</p>
<h3>Salidas</h3>
<dl class="message-properties">
<dt>payload</dt>
<dd>Para una solicitud GET, contiene un objeto con cualquier parámetro de cadena de consulta.
De lo contrario, contiene el cuerpo de la solicitud HTTP.</dd>
<dt>req<span class="property-type">objeto</span></dt>
<dd>Un objeto de solicitud HTTP. Este objeto contiene varias propiedades que proporcionan información sobre la solicitud.
<ul>
<li><code>body</code> - el cuerpo de la solicitud entrante. El formato dependerá de la solicitud.</li>
<li><code>headers</code> - un objeto que contiene los encabezados de solicitud HTTP.</li>
<li><code>query</code> - un objeto que contiene cualquier parámetro de cadena de consulta.</li>
<li><code>params</code> - un objeto que contiene cualquier parámetro de ruta.</li>
<li><code>cookies</code> - un objeto que contiene las cookies para la solicitud.</li>
<li><code>files</code> - si está habilitado dentro del nodo, un objeto que contiene los archivos cargados como parte de una solicitud POST.</li>
</ul>
</dd>
<dt>res<span class="property-type">objeto</span></dt>
<dd>Un objeto de respuesta HTTP. Esta propiedad no debe usarse directamente; el nodo <code>Respuesta HTTP</code> documenta cómo responder a una solicitud.
Esta propiedad debe permanecer adjunta al mensaje pasado al nodo de respuesta.</dd>
</dl>
<h3>Detalles</h3>
<p>El nodo escuchará en la ruta configurada solicitudes de un tipo particular. La ruta se puede especificar completamente, como <code>/user</code>, o incluir parámetros con nombre que acepten cualquier valor, como <code>/user/:name</code>. Cuando se utilizan parámetros con nombre, se puede acceder a su valor real en una solicitud en <code>msg.req.params</code>.</p>
<p>Para solicitudes que incluyen un cuerpo, como POST o PUT, el contenido de la solicitud está disponible como <code>msg.payload</code>.</p>
<p>Si se puede determinar el tipo de contenido de la solicitud, el cuerpo se analizará a cualquier tipo apropiado. Por ejemplo, <code>application/json</code> se analizará según su representación de objeto JavaScript.</p>
<p><b>Nota:</b> este nodo no envía ninguna respuesta a la solicitud. El flujo debe incluir un nodo de respuesta HTTP para completar la solicitud.</p>
</script>
<script type="text/html" data-help-name="http response">
<p>Envía respuestas a las solicitudes recibidas desde un nodo de entrada HTTP.</p>
<h3>Entradas</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">texto</span></dt>
<dd>El cuerpo de la respuesta.</dd>
<dt class="optional">statusCode <span class="property-type">número</span></dt>
<dd>Si se establece, se utiliza como código de estado de respuesta. Predeterminado: 200.</dd>
<dt class="optional">headers <span class="property-type">objeto</span></dt>
<dd>Si está configurado, proporciona encabezados HTTP para incluirlos en la respuesta.</dd>
<dt class="optional">cookies <span class="property-type">objeto</span></dt>
<dd>Si está configurado, se puede utilizar para configurar o eliminar cookies.</dd>
</dl>
<h3>Detalles</h3>
<p>El <code>statusCode</code> y los <code>headers</code> también se pueden configurar dentro del propio nodo. Si una propiedad se establece dentro del nodo, la propiedad del mensaje correspondiente no puede anularla.</p>
<h4>Manejo de cookies</h4>
<p>La propiedad <code>cookies</code> debe ser un objeto de pares nombre/valor.
El valor puede ser una cadena para establecer el valor de la cookie con opciones predeterminadas o puede ser un objeto de opciones.</p>
<p>El siguiente ejemplo establece dos cookies: una llamada <code>name</code> con un valor de <code>nick</code>, la otra llamada <code>session</code> con un valor de <code >1234</code> y un vencimiento establecido en 15 minutos.</p>
<pre>
msg.cookies = {
name: 'nick',
session: {
value: '1234',
maxAge: 900000
}
}</pre>
<p>Las opciones válidas incluyen:</p>
<ul>
<li><code>domain</code> - (texto) nombre de dominio para la cookie</li>
<li><code>expires</code> - (fecha) fecha de vencimiento en GMT. Si no se especifica o se establece en 0, crea una cookie de sesión</li>
<li><code>maxAge</code> - (texto) fecha de vencimiento en relación con la hora actual en milisegundos</li>
<li><code>path</code> - (texto) ruta para la cookie. El valor predeterminado es /</li>
<li><code>value</code> - (texto) el valor a utilizar para la cookie</li>
</ul>
<p>Para eliminar una cookie, establece su <code>valor</code> en <code>null</code>.</p>
</script>

View File

@@ -0,0 +1,81 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
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/html" data-help-name="http request">
<p>Envía solicitudes HTTP y devuelve la respuesta.</p>
<h3>Entradas</h3>
<dl class="message-properties">
<dt class="optional">url <span class="property-type">texto</span></dt>
<dd>Si no está configurada en el nodo, esta propiedad opcional establece la URL de la solicitud.</dd>
<dt class="optional">method <span class="property-type">texto</span></dt>
<dd>Si no está configurada en el nodo, esta propiedad opcional establece el método HTTP de la solicitud.
Debe ser uno de los siguientes: <code>GET</code>, <code>PUT</code>, <code>POST</code>, <code>PATCH</code> o <code>DELETE</code>.</dd>
<dt class="optional">headers <span class="property-type">objeto</span></dt>
<dd>Establece los encabezados HTTP de la solicitud. NOTA: Cualquier encabezado establecido en la configuración del nodo sobrescribirá cualquier encabezado coincidente en <code>msg.headers</code> </dd>
<dt class="optional">cookies <span class="property-type">objeto</span></dt>
<dd>Si está configurado, se puede utilizar para enviar cookies con la solicitud.</dd>
<dt class="optional">payload</dt>
<dd>Enviado como cuerpo de la solicitud.</dd>
<dt class="optional">rejectUnauthorized</dt>
<dd>Si se establece en <code>falso</code>, permite realizar solicitudes a sitios https que utilizan certificados autofirmados.</dd>
<dt class="optional">followRedirects</dt>
<dd>Si se establece en <code>falso</code>, se impide seguir el redireccionamiento (HTTP 301).<code>verdadero</code> de forma predeterminada</dd>
<dt class="optional">requestTimeout</dt>
<dd>Si se establece en un número positivo de milisegundos, anulará el parámetro <code>httpRequestTimeout</code> establecido globalmente.</dd>
</dl>
<h3>Salidas</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">texto | objeto | buffer</span></dt>
<dd>El cuerpo de la respuesta. El nodo se puede configurar para devolver el cuerpo como una cadena, intentar analizarlo como una cadena JSON o dejarlo como un búfer binario.</dd>
<dt>statusCode <span class="property-type">número</span></dt>
<dd>El código de estado de la respuesta o el código de error si no se pudo completar la solicitud.</dd>
<dt>headers <span class="property-type">objeto</span></dt>
<dd>Un objeto que contiene los encabezados de respuesta.</dd>
<dt>responseUrl <span class="property-type">texto</span></dt>
<dd>En caso de que se produzcan redirecciones durante el procesamiento de la solicitud, esta propiedad es la URL redirigida final.
De lo contrario, la URL de la solicitud original.</dd>
<dt>responseCookies <span class="property-type">objeto</span></dt>
<dd>Si la respuesta incluye cookies, esta propiedad es un objeto de pares de nombre/valor para cada cookie.</dd>
<dt>redirectList <span class="property-type">matriz</span></dt>
<dd>Si la solicitud fue redirigida una o más veces, la información acumulada se agregará en esta propiedad. `location` es el siguiente destino de redireccionamiento. `cookies` son las cookies devueltas por la fuente de redireccionamiento.</dd>
</dl>
<h3>Detalles</h3>
<p>Cuando se configura dentro del nodo, la propiedad URL puede contener etiquetas <a href="http://mustache.github.io/mustache.5.html" target="_blank">mustache</a>. Estos permiten construir la URL utilizando valores del mensaje entrante. Por ejemplo, si la URL está configurada en <code>example.com/{{{topic}}}</code>, se insertará automáticamente el valor de <code>msg.topic</code>.
Usar {{{...}}} evita que el mustache codifique caracteres como / & etc.</p>
<p>Opcionalmente, el nodo puede codificar automáticamente <code>msg.payload</code> como parámetros de cadena de consulta para una solicitud GET, en cuyo caso <code>msg.payload</code> tiene que ser un objeto.</p>
<p><b>Nota</b>: Si se ejecuta detrás de un proxy, se debe configurar la variable de entorno estándar <code>http_proxy=...</code> y reiniciar Node-RED, o usar la configuración de proxy. Si hay una configuración de proxy definidia, tiene prioridad sobre la variable de entorno.</p>
<h4>Usar múltiples nodos de solicitud HTTP</h4>
<p>Para utilizar más de uno de estos nodos en el mismo flujo, se debe tener cuidado con la propiedad <code>msg.headers</code>. El primer nodo establecerá esta propiedad con los encabezados de respuesta. El siguiente nodo utilizará esos encabezados para su solicitud; esto no suele ser lo correcto. Si la propiedad <code>msg.headers</code> no se modifica entre nodos, el segundo nodo la ignorará. Para configurar encabezados personalizados, primero se debe eliminar <code>msg.headers</code> o restablecerlo a un objeto vacío: <code>{}</code>.
<h4>Manejo de cookies</h4>
<p>La propiedad <code>cookies</code> pasada al nodo debe ser un objeto de pares nombre/valor.
El valor puede ser una cadena para establecer el valor de la cookie o puede ser un objeto con una única propiedad <code>value</code>.</p>
<p>Cualquier cookie devuelto por la solicitud se guarda en la propiedad <code>responseCookies</code>.</p>
<h4>Manejo del tipo de contenido</h4>
<p>Si <code>msg.payload</code> es un objeto, el nodo establecerá automáticamente el tipo de contenido de la solicitud en <code>application/json</code> y codificará el cuerpo como tal.</p >
<p>Para codificar la solicitud como datos de formulario, <code>msg.headers["content-type"]</code> debe establecerse en <code>application/x-www-form-urlencoded</code>.< /p>
<h4>Subir archivo</h4>
<p>Para realizar una carga de archivos, <code>msg.headers["content-type"]</code> debe configurarse en <code>multipart/form-data</code> y <code>msg.payload</code > pasado al nodo debe ser un objeto con la siguiente estructura:</p>
<pre><code>{
"KEY": {
"value": FILE_CONTENTS,
"options": {
"filename": "FILENAME"
}
}
}</code></pre>
<p>Los valores de <code>KEY</code>, <code>FILE_CONTENTS</code> y <code>FILENAME</code> deben establecerse en los valores apropiados.</p>
</script>

View File

@@ -0,0 +1,36 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
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/html" data-help-name="websocket in">
<p>Nodo de entrada WebSocket.</p>
<p>De forma predeterminada, los datos recibidos del WebSocket estarán en <code>msg.payload</code>.
El socket se puede configurar para esperar una cadena JSON formada correctamente, en cuyo caso analizará el JSON y enviará el objeto resultante como el mensaje completo.</p>
</script>
<script type="text/html" data-help-name="websocket out">
<p>Nodo de salida WebSocket.</p>
<p>De forma predeterminada, <code>msg.payload</code> se enviará a través del WebSocket. El socket se puede configurar para codificar todo el objeto <code>msg</code> como una cadena JSON y enviarlo a través del WebSocket.</p>
<p>Si el mensaje que llega a este nodo comenzó en un nodo WebSocket In, el mensaje se enviará de vuelta al cliente que desencadenó el flujo. De lo contrario, el mensaje se transmitirá a todos los clientes conectados.</p>
<p>Si quieres transmitir un mensaje que comenzó en un nodo WebSocket In, debes eliminar la propiedad <code>msg._session</code> del flujo.</p>
</script>
<script type="text/html" data-help-name="websocket-listener">
<p>Este nodo de configuración crea un punto final de WebSocket Server utilizando la ruta especificada.</p>
</script>
<script type="text/html" data-help-name="websocket-client">
<p>Este nodo de configuración conecta un cliente WebSocket a la URL especificada.</p>
</script>

View File

@@ -0,0 +1,35 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
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/html" data-help-name="tcp in">
<p>Proporciona una selección de entradas TCP. Puede conectarse a un puerto TCP remoto o aceptar conexiones entrantes.</p>
<p><b>Nota: </b>En algunos sistemas, es posible que necesites acceso raíz o de administrador para acceder a los puertos inferiores a 1024.</p>
</script>
<script type="text/html" data-help-name="tcp out">
<p>Proporciona una selección de salidas TCP. Puede conectarse a un puerto TCP remoto, aceptar conexiones entrantes o responder a mensajes recibidos desde un nodo TCP In.</p>
<p>Solo se envía el <code>msg.payload</code>.</p>
<p>Si <code>msg.payload</code> es una cadena que contiene una codificación Base64 de datos binarios, la opción de decodificación Base64 hará que se vuelva a convertir a binario antes de enviarse.</p>
<p>Si <code>msg._session</code> no está presente, la carga se envía a <b>todos</b> los clientes conectados.</p>
<p><b>Nota: </b>En algunos sistemas, es posible que necesites acceso raíz o de administrador para acceder a los puertos inferiores a 1024.</p>
</script>
<script type="text/html" data-help-name="tcp request">
<p>Un nodo de solicitud TCP simple: envía el <code>msg.payload</code> a un puerto tcp del servidor y espera una respuesta.</p>
<p>Se conecta, envía la "solicitud" y lee la "respuesta". Puede contar una cantidad de caracteres devueltos en un búfer fijo, hacer coincidir un carácter específico antes de regresar, esperar un tiempo de espera fijo desde la primera respuesta y luego regresar, esperar datos, o enviar y luego cerrar la conexión inmediatamente, sin esperar una respuesta.</p>
<p>La respuesta se generará en <code>msg.payload</code> como un búfer, por lo que es posible que quieras utilizar .toString().</p>
<p>Si dejas el host TCP o el puerto en blanco, debes configurarlos utilizando las propiedades <code>msg.host</code> y <code>msg.port</code> en cada mensaje enviado al nodo.</p>
</script>

View File

@@ -0,0 +1,28 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
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/html" data-help-name="udp in">
<p>Un nodo de entrada UDP que produce un <code>msg.payload</code> que contiene un búfer, una cadena o una cadena codificada en base64. Admite multidifusión.</p>
<p>También proporciona <code>msg.ip</code> y <code>msg.port</code> configurados para la dirección IP y el puerto desde el que se recibió el mensaje.</p>
<p><b>Nota</b>: En algunos sistemas, es posible que necesites acceso de administrador para usar puertos inferiores a 1024 y/o transmisión.</p>
</script>
<script type="text/html" data-help-name="udp out">
<p>Este nodo envía <code>msg.payload</code> al host y puerto UDP designado. Admite multidifusión.</p>
<p>También puede utilizar <code>msg.ip</code> y <code>msg.port</code> para establecer los valores de destino, pero los valores configurados estáticamente tienen prioridad.</p>
<p>Si seleccionas transmisión, establece la dirección IP de transmisión local o tal vez prueba con 255.255.255.255, que es la dirección de transmisión global.</p>
<p><b>Nota</b>: En algunos sistemas, es posible que necesites ser root para usar puertos inferiores a 1024 y/o transmisión.</p>
</script>

View File

@@ -0,0 +1,49 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
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/html" data-help-name="csv">
<p>Convierte entre una cadena con formato CSV y su representación de objeto JavaScript, en cualquier dirección.</p>
<h3>Entradas</h3>
<dl class="message-properties">
<dt>payload<span class="property-type">objeto | matriz | texto</span></dt>
<dd>Un objeto JavaScript, una matriz o una cadena CSV.</dd>
</dl>
<h3>Salidas</h3>
<dl class="message-properties">
<dt>payload<span class="property-type">objeto | matriz | texto</span></dt>
<dd>
<ul>
<li>Si la entrada es una cadena, intenta analizarla como CSV y crea un objeto JavaScript de pares clave/valor para cada línea.
Luego, el nodo enviará un mensaje para cada línea o un mensaje único que contenga una serie de objetos.</li>
<li>Si la entrada es un objeto JavaScript, intenta crear una cadena CSV.</li>
<li>Si la entrada es una matriz de valores simples, crea una cadena CSV de una sola línea.</li>
<li>Si la entrada es una matriz de matrices o una matriz de objetos, se crea una cadena CSV de varias líneas.</li>
</ul>
</dd>
</dl>
<h3>Detalles</h3>
<p>La plantilla de columnas puede contener una lista ordenada de nombres de columnas. Al convertir CSV en un objeto, los nombres de las columnas se utilizarán como nombres de propiedades. Alternativamente, los nombres de las columnas se pueden tomar de la primera fila del CSV.</p>
<p>Al convertir a CSV, la plantilla de columnas se utiliza para identificar qué propiedades extraer del objeto y en qué orden.</p>
<p>Si la plantilla de columnas está en blanco, puede utilizar una lista simple de propiedades separadas por comas proporcionada en <code>msg.columns</code> para determinar qué extraer y en qué orden. Si ninguno de los dos está presente, todas las propiedades del objeto se muestran en el orden en que se encuentran en la primera fila.</p>
<p>Si la entrada es una matriz, entonces la plantilla de columnas solo se usa para generar opcionalmente una fila de títulos de columnas.</p>
<p>Si se marca la opción 'analizar valores numéricos', los valores numéricos de cadena se devolverán como números, es decir. valor de enmedio '1, "1,5", 2'.</p>
<p>Si se marca la opción 'incluir cadenas vacías', se devolverán cadenas vacías como resultado, es decir. valor de enmedio '"1","",3'.</p>
<p>Si se marca la opción 'incluir valores nulos', se devolverán valores nulos como resultado, es decir. valor de enmedio '"1",,3'.</p>
<p>El nodo puede aceptar una entrada de varias partes siempre que la propiedad <code>parts</code> esté configurada correctamente, por ejemplo, desde un nodo de entrada de archivo o un nodo dividido.</p>
<p>Si genera varios mensajes, tendrán su propiedad <code>parts</code> configurada y formarán una secuencia de mensajes completa.</p>
<p>Si el nodo está configurado para enviar encabezados de columna solo una vez, si se configura <code>msg.reset</code> en cualquier valor hará que el nodo reenvíe los encabezados.</p>
<p><b>Nota:</b> la plantilla de columna debe estar separada por comas, incluso si se elige un separador diferente para los datos.</p>
</script>

View File

@@ -0,0 +1,33 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
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/html" data-help-name="html">
<p>Extrae elementos de un documento HTML contenido en <code>msg.payload</code> usando un selector CSS.</p>
<h3>Entradas</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">texto</span></dt>
<dd>la cadena HTML de la que extraer elementos.</dd>
<dt class="optional">select <span class="property-type">texto</span></dt>
<dd>Si no está configurado en el panel de edición, el selector se puede configurar como esta propiedad de msg.</dd>
</dl>
<h3>Salidas</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">matriz | texto</span></dt>
<dd>el resultado puede ser un único mensaje con una carga que contenga una matriz de elementos coincidentes o varios mensajes que contengan cada uno un elemento coincidente. Si se envían varios mensajes, también tendrán <code>parts</code> configuradas.</dd>
</dl>
<h3>Detalles</h3>
<p>Este nodo admite una combinación de selectores CSS y jQuery. Consulta la <a href="https://github.com/fb55/CSSselect#user-content-supported-selectors" target="_blank">documentación de css-select</a> para obtener más información sobre la sintaxis admitida.</p>
</script>

View File

@@ -0,0 +1,44 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
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/html" data-help-name="json">
<p>Convierte entre una cadena JSON y su representación de objeto JavaScript, en cualquier dirección.</p>
<h3>Entradas</h3>
<dl class="message-properties">
<dt>payload<span class="property-type">objeto | texto</span></dt>
<dd>Un objeto JavaScript o una cadena JSON.</dd>
<dt>schema<span class="property-type">objeto</span></dt>
<dd>Un objeto de esquema JSON opcional para validar la carga.
La propiedad se eliminará antes de que el <code>msg</code> se envíe al siguiente nodo.</dd>
</dl>
<h3>Salidas</h3>
<dl class="message-properties">
<dt>payload<span class="property-type">objeto | texto</span></dt>
<dd>
<ul>
<li>Si la entrada es una cadena JSON, intenta analizarla en un objeto JavaScript.</li>
<li>Si la entrada es un objeto JavaScript, crea una cadena JSON. Opcionalmente, la cadena se puede formatear.</li>
</ul>
</dd>
<dt>schemaError<span class="property-type">matriz</span></dt>
<dd>Si falla la validación del esquema JSON, el nodo cpatura (catch) tendrá una propiedad <code>schemaError</code> que contiene una serie de errores.</dd>
</dl>
<h3>Detalles</h3>
<p>De forma predeterminada, el nodo opera en <code>msg.payload</code>, pero se puede configurar para convertir cualquier propiedad de mensaje.</p>
<p>El nodo también se puede configurar para garantizar una codificación particular en lugar de alternar entre las dos. Esto se puede usar, por ejemplo, con el nodo <code>HTTP In</code> para garantizar que la carga sea un objeto analizado incluso si una solicitud entrante no configuró su tipo de contenido correctamente para que el nodo HTTP In realice la conversión.</p>
<p>Si el nodo está configurado para garantizar que la propiedad esté codificada como una cadena y recibe una cadena, no se realizarán más comprobaciones de la propiedad. No comprobará que la cadena sea JSON válida ni la reformateará si se selecciona la opción de formato.</p>
<p>Para más detalles sobre el esquema JSON puedes consultar la especificación <a href="http://json-schema.org/latest/json-schema-validation.html">aquí</a>.</p>
</script>

View File

@@ -0,0 +1,49 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
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/html" data-help-name="xml">
<p>Convierte entre una cadena XML y su representación de objeto JavaScript, en cualquier dirección.</p>
<h3>Entradas</h3>
<dl class="message-properties">
<dt>payload<span class="property-type">objeto | texto</span></dt>
<dd>Un objeto JavaScript o una cadena XML.</dd>
<dt class="optional">options <span class="property-type">objeto</span></dt>
<dd>Esta propiedad opcional se puede utilizar para pasar cualquiera de las opciones admitidas por la librería subyacente utilizada para convertir hacia y desde XML. Consulta <a href="https://github.com/Leonidas-from-XIV/node-xml2js/blob/master/README.md#options" target="_blank">los documentos xml2js</a> para obtener más información.</dd>
</dl>
<h3>Salidas</h3>
<dl class="message-properties">
<dt>payload<span class="property-type">objeto | texto</span></dt>
<dd>
<ul>
<li>Si la entrada es una cadena, intenta analizarla como XML y crea un objeto JavaScript.</li>
<li>Si la entrada es un objeto JavaScript, intenta construir una cadena XML.</li>
</ul>
</dd>
</dl>
<h3>Detalles</h3>
<p>Al convertir entre XML y un objeto, cualquier atributo XML se agrega como una propiedad denominada <code>$</code> de forma predeterminada.
Cualquier contenido de texto se agrega como una propiedad denominada <code>_</code>. Estos nombres de propiedades se pueden especificar en la configuración del nodo.</p>
<p>Por ejemplo, el siguiente XML se convertirá como se muestra:</p>
<pre>&lt;p class="tag"&gt;Hello World&lt;/p&gt;</pre>
<pre>{
"p": {
"$": {
"class": "tag"
},
"_": "Hello World"
}
}</pre>
</script>

View File

@@ -0,0 +1,34 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
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/html" data-help-name="yaml">
<p>Convierte entre una cadena con formato YAML y su representación de objeto JavaScript, en cualquier dirección.</p>
<h3>Entradas</h3>
<dl class="message-properties">
<dt>payload<span class="property-type">objeto | texto</span></dt>
<dd>Un objeto JavaScript o una cadena YAML.</dd>
</dl>
<h3>Salidas</h3>
<dl class="message-properties">
<dt>payload<span class="property-type">objeto | texto</span></dt>
<dd>
<ul>
<li>Si la entrada es una cadena YAML, intenta convertirla en un objeto JavaScript.</li>
<li>Si la entrada es un objeto JavaScript, crea una cadena YAML.</li>
</ul>
</dd>
</dl>
</script>

View File

@@ -0,0 +1,146 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
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/html" data-help-name="split">
<p>Divide un mensaje en una secuencia de mensajes.</p>
<h3>Entradas</h3>
<dl class="message-properties">
<dt>payload<span class="property-type">objeto | texto | matriz | búfer</span></dt>
<dd>El comportamiento del nodo está determinado por el tipo de <code>msg.payload</code>:
<ul>
<li><b>texto</b>/<b>búfer</b> - el mensaje se divide utilizando el carácter especificado (predeterminado: <code>\n</code>), secuencia de búfer o en longitudes fijas.</li>
<li><b>matriz</b> - el mensaje se divide en elementos de matriz individuales o en matrices de longitud fija.</li>
<li><b>objeto</b> - Se envía un mensaje para cada par clave/valor del objeto.</li>
</ul>
</dd>
</dl>
<h3>Salidas</h3>
<dl class="message-properties">
<dt>parts<span class="property-type">objeto</span></dt>
<dd>Esta propiedad contiene información sobre cómo se dividió el mensaje original. Si se pasa al nodo <b>unir</b>, la secuencia se puede volver a ensamblar en un solo mensaje. Tiene las siguientes propiedades:
<ul>
<li><code>id</code> - un identificador para el grupo de mensajes</li>
<li><code>index</code> - la posición dentro del grupo</li>
<li><code>count</code> - si se conoce, el número total de mensajes en el grupo. Consulta 'modo de transmisión' a continuación.</li>
<li><code>type</code> - el tipo de mensaje: texto/matriz/objeto/búfer</li>
<li><code>ch</code> - para una cadena o un búfer, los datos utilizados para dividir el mensaje como una cadena o una matriz de bytes</li>
<li><code>key</code> - para un objeto, la clave de la propiedad desde la que se creó este mensaje. El nodo se puede configurar para copiar también este valor en otras propiedades del mensaje, como <code>msg.topic</code>.</li>
<li><code>len</code> - la longitud de cada mensaje cuando se divide utilizando un valor de longitud fija</li>
</ul>
</dd>
</dl>
<h3>Detalles</h3>
<p>Este nodo facilita la creación de un flujo que realiza acciones comunes en una secuencia de mensajes antes de usar el nodo <b>unir</b> y recombinar la secuencia en un solo mensaje.</p>
<p>Utiliza la propiedad <code>msg.parts</code> para rastrear las partes individuales de una secuencia.</p>
<h4>Modo de transmisión</h4>
<p>El nodo también se puede utilizar para redistribuir un flujo de mensajes. Por ejemplo, un dispositivo serie que envía comandos terminados en nueva línea puede entregar un único mensaje con un comando parcial al final. En el 'modo de transmisión', este nodo dividirá un mensaje y enviará cada segmento completo. Si hay un segmento parcial al final, el nodo lo conservará y lo antepondrá al siguiente mensaje que se reciba.</p>
<p>Cuando se opera en este modo, el nodo no establecerá la propiedad <code>msg.parts.count</code> ya que no sabe cuántos mensajes esperar en la secuencia. Esto significa que no se puede utilizar con el nodo <b>unir</b> en su modo automático.</p>
</script>
<script type="text/html" data-help-name="join">
<p>Une secuencias de mensajes en un solo mensaje.</p>
<p>Hay tres modos disponibles:</p>
<dl>
<dt>automático</dt>
<dd>Cuando se empareja con el nodo <b>dividr</b>, unirá automáticamente los mensajes para revertir la división que se realizó.</dd>
<dt>manual</dt>
<dd>Une secuencias de mensajes de diversas formas.</dd>
<dt>reducir secuencia</dt>
<dd>Aplica una expresión a todos los mensajes de una secuencia para reducirla a un solo mensaje.</dd>
</dl>
<h3>Entradas</h3>
<dl class="message-properties">
<dt class="optional">parts<span class="property-type">objeto</span></dt>
<dd>Para unir automáticamente una secuencia de mensajes, todos deben tener esta propiedad establecida. El nodo <b>dividr</b> genera esta propiedad pero se puede crear manualmente. Tiene las siguientes propiedades:
<ul>
<li><code>id</code> - un identificador para el grupo de mensajes</li>
<li><code>index</code> - la posición dentro del grupo</li>
<li><code>count</code> - el número total de mensajes en el grupo</li>
<li><code>type</code> - el tipo de mensaje: cadena/matriz/objeto/búfer</li>
<li><code>ch</code> - para una cadena o un búfer, los datos utilizados para dividir el mensaje como una cadena o una matriz de bytes</li>
<li><code>key</code> - para un objeto, la clave de la propiedad desde la que se creó este mensaje</li>
<li><code>len</code> - la longitud de cada mensaje cuando se divide utilizando un valor de longitud fija</li>
</ul>
</dd>
<dt class="optional">completa</dt>
<dd>Si está configurado, el nodo agregará la carga y luego enviará el mensaje de salida en su estado actual.
Si no quieres añadir la carga, elimínala del mensaje.</dd>
<dt class="optional">reset</dt>
<dd>Si se establece, el nodo borrará cualquier mensaje parcialmente completo y no lo enviará.</dd>
<dt class="optional">restartTimeout</dt>
<dd>Si está configurado y el nodo tiene un tiempo de espera configurado, ese tiempo de espera se reiniciará.</dd>
</dl>
<h3>Detalles</h3>
<h4>Modo Automático</h4>
<p>El modo automático utiliza la propiedad <code>parts</code> de los mensajes entrantes para determinar cómo se debe unir la secuencia. Esto te permite revertir automáticamente la acción de un nodo <b>dividido</b>.</p>
<h4>Modo Manual</h4>
<p>Cuando se configura para unirse en modo manual, el nodo puede unir secuencias de mensajes en varios resultados diferentes:</p>
<ul>
<li>a <b>texto</b> o <b>buffer</b> - creado uniendo la propiedad seleccionada de cada mensaje con los caracteres de unión o el búfer especificados.</li>
<li>an <b>matriz</b> - creado agregando cada propiedad seleccionada, o mensaje completo, a la matriz de salida.</li>
<li>a <b>objeto clave/valor</b> - creado utilizando una propiedad de cada mensaje para determinar la clave bajo la cual se almacena el valor requerido.</li>
<li>a <b>objeto combinado</b> - creado fusionando la propiedad de cada mensaje en un solo objeto.</li>
</ul>
<p>Las otras propiedades del mensaje de salida se toman del último mensaje recibido antes de enviar el resultado.</p>
<p>Se puede establecer un <i>count</i> para determinar cuántos mensajes se deben recibir antes de generar el mensaje de salida.
Para las salidas de objetos, una vez que se haya alcanzado este recuento, el nodo se puede configurar para enviar un mensaje por cada mensaje recibido a continuación.</p>
<p>Se puede establecer un <i>tiempo de espera</i> para activar el envío del nuevo mensaje utilizando lo que se haya recibido hasta el momento.
Este tiempo de espera se puede reiniciar enviando un mensaje con la propiedad <code>msg.restartTimeout</code> establecida.</p>
<p>Si se recibe un mensaje con la propiedad <code>msg.complete</code> configurada, el mensaje de salida se finaliza y se envía.
Esto restablece el recuento de segmentos.</p>
<p>Si se recibe un mensaje con la propiedad <code>msg.reset</code> configurada, el mensaje parcialmente completo se elimina y no se envía.
Esto restablece el recuento de segmentos.</p>
<h4>Modo Reducir Secuencia</h4>
<p>Cuando se configura para unirse en modo de reducción, se aplica una expresión a cada mensaje en una secuencia y el resultado se acumula para producir un solo mensaje.</p>
<dl class="message-properties">
<dt>Valor inicial</dt>
<dd>El valor inicial del valor acumulado. (<code>$A</code>).</dd>
<dt>Expresión de reducción</dt>
<dd>Una expresión JSONata que se llama para cada mensaje de la secuencia.
El resultado se pasa a la siguiente llamada de la expresión como valor acumulado.
En la expresión, se pueden utilizar las siguientes variables especiales:
<ul>
<li><code>$A</code>: el valor acumulado,</li>
<li><code>$I</code>: índice del mensaje en la secuencia,</li>
<li><code>$N</code>: número de mensajes en la secuencia.</li>
</ul>
</dd>
<dt>Expresión de reparación</dt>
<dd>Una expresión JSONata opcional que se aplica después de que la expresión de reducción se haya aplicado a todos los mensajes de la secuencia.
En la expresión, se pueden utilizar las siguientes variables especiales:
<ul>
<li><code>$A</code>: el valor acumulado,</li>
<li><code>$N</code>: número de mensajes en la secuencia.</li>
</ul>
</dd>
<p>De forma predeterminada, la expresión de reducción se aplica en orden, desde el primero hasta el último mensaje de la secuencia. Opcionalmente se puede aplicar en orden inverso.</p>
<p>$N es el número de mensajes que llegan, incluso si son idénticos.</p>
</dl>
<p><b>Ejemplo:</b> la siguiente configuración, dada una secuencia de valores numéricos, calcula el valor promedio:
<ul>
<li><b>Expresión de reducción</b>: <code>$A+payload</code></li>
<li><b>Valor inicial</b>: <code>0</code></li>
<li><b>Expresión de reparación</b>: <code>$A/$N</code></li>
</ul>
</p>
<h4>Almacenar mensajes</h4>
<p>Este nodo almacenará en el búfer los mensajes internamente para poder trabajar en secuencias. La configuración de tiempo de ejecución <code>nodeMessageBufferMaxLength</code> se puede utilizar para limitar cuántos mensajes se almacenarán en el buffer.</p>
</script>

View File

@@ -0,0 +1,41 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
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/html" data-help-name="sort">
<p>Una función que ordena las propiedades del mensaje o una secuencia de mensajes.</p>
<p>Cuando se configura para ordenar la propiedad del mensaje, el nodo ordena los datos de la matriz a los que apunta la propiedad del mensaje especificado.</p>
<p>Cuando se configura para ordenar una secuencia de mensajes, reordenará los mensajes.</p>
<p>El orden de clasificación puede ser:</p>
<ul>
<li><b>ascendente</b>,</li>
<li><b>descendente</b>.</li>
</ul>
<p>Para números, el orden numérico se puede especificar mediante una casilla de verificación.</p>
<p>La clave de ordenación puede ser un valor de elemento o una expresión JSONata para ordenar el valor de una propiedad, o una propiedad de mensaje o una expresión JSONata para ordenar una secuencia de mensajes.<p>
<p>Al ordenar una secuencia de mensajes, el nodo de ordenación depende de que los mensajes recibidos tengan <code>msg.parts</code> configurado. El nodo dividido genera esta propiedad, pero se puede crear manualmente. Tiene las siguientes propiedades:</p>
<p>
<ul>
<li><code>id</code> - un identificador para el grupo de mensajes</li>
<li><code>index</code> - la posición dentro del grupo</li>
<li><code>count</code> - el número total de mensajes en el grupo</li>
</ul>
</p>
<p><b>Nota:</b> Este nodo guarda internamente mensajes para su funcionamiento. Para evitar un uso inesperado de la memoria, se puede especificar la cantidad máxima de mensajes conservados. El valor predeterminado no es límite en la cantidad de mensajes.
<ul>
Propiedad <li><code>nodeMessageBufferMaxLength</code> establecida en <b>settings.js</b>.</li>
</ul>
</p>
</script>

View File

@@ -0,0 +1,35 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
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/html" data-help-name="batch">
<p>Crea secuencias de mensajes basadas en varias reglas.</p>
<h3>Detalles</h3>
<p>Hay tres modos para crear secuencias de mensajes:</p>
<dl>
<dt>Número de mensajes</dt>
<dd>agrupa mensajes en secuencias de una longitud determinada. La opción <b>superposición</b> especifica cuántos mensajes al final de una secuencia deben repetirse al comienzo de la siguiente secuencia.</dd>
<dt>Intervalo de tiempo</dt>
<dd>agrupa los mensajes que llegan dentro del intervalo especificado. Si no llega ningún mensaje dentro del intervalo, el nodo puede opcionalmente enviar un mensaje vacío.</dd>
<dt>Concatenar secuencias</dt>
<dd>crea una secuencia de mensajes concatenando secuencias entrantes. Cada mensaje debe tener una propiedad <code>msg.topic</code> y una propiedad <code>msg.parts</code> que identifique su secuencia. El nodo está configurado con una lista de valores de <code>topic</code> para identificar las secuencias de orden que están concatenadas.
</dd>
</dl>
<h4>Almacenar mensajes</h4>
<p>Este nodo almacenará en búfer los mensajes internamente para poder trabajar en secuencias. La configuración de tiempo de ejecución <code>nodeMessageBufferMaxLength</code> se puede utilizar para limitar cuántos nodos de mensajes almacenarán en el buffer.</p>
<p>Si se recibe un mensaje con la propiedad <code>msg.reset</code> configurada, los mensajes almacenados en el búfer se eliminan y no se envían.</p>
</script>

View File

@@ -0,0 +1,62 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
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/html" data-help-name="file">
<p>Escribe <code>msg.payload</code> en un archivo, ya sea agregándolo al final o reemplazando el contenido existente.
Alternativamente, puede eliminar el archivo.</p>
<h3>Entradas</h3>
<dl class="message-properties">
<dt class="optional">filename <span class="property-type">texto</span></dt>
<dd>El nombre del archivo que se actualizará se puede proporcionar en la configuración del nodo o como una propiedad del mensaje.
De forma predeterminada utilizará <code>msg.filename</code> pero esto se puede personalizar en el nodo.
</dd>
<dt class="optional">encoding <span class="property-type">texto</span></dt>
<dd>Si la codificación está configurada para que se establezca mediante mensaje, entonces esta propiedad opcional puede establecer la codificación.</dt>
</dl>
<h3>Salidas</h3>
<p>Al finalizar la escritura, el mensaje de entrada se envía al puerto de salida.</p>
<h3>Detalles</h3>
<p>Cada carga de mensaje se agregará al final del archivo, añadiendo opcionalmente un carácter de nueva línea (\n) entre cada uno.</p>
<p>Si se utiliza <code>msg.filename</code>, el archivo se cerrará después de cada escritura. Para obtener el mejor rendimiento, utiliza un nombre de archivo fijo.</p>
<p>Se puede configurar para sobrescribir el archivo completo en lugar de agregarlo. Por ejemplo, al escribir datos binarios en un archivo, como una imagen, se debe usar esta opción y la opción de agregar una nueva línea debe estar deshabilitada.</p>
<p>La codificación de los datos escritos en un archivo se puede especificar desde la lista de codificaciones.</p>
<p>Como alternativa, este nodo se puede configurar para eliminar el archivo.</p>
</script>
<script type="text/html" data-help-name="file in">
<p>Lee el contenido de un archivo como una cadena o un búfer binario.</p>
<h3>Entradas</h3>
<dl class="message-properties">
<dt class="optional">filename <span class="property-type">texto</span></dt>
<dd>El nombre del archivo que se va a leer se puede proporcionar en la configuración del nodo o como una propiedad del mensaje.
De forma predeterminada utilizará <code>msg.filename</code> pero esto se puede personalizar en el nodo.
</dd>
</dl>
<h3>Salidas</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">texto | buffer</span></dt>
<dd>El contenido del archivo como una cadena o un búfer binario.</dd>
<dt class="optional">filename <span class="property-type">texto</span></dt>
<dd>Si no está configurada en el nodo, esta propiedad opcional establece el nombre del archivo que se leerá.</dd>
</dl>
<h3>Detalles</h3>
<p>El nombre del archivo debe ser una ruta absoluta; de lo contrario, será relativa al directorio de trabajo del proceso Node-RED.</p>
<p>En Windows, es posible que sea necesario utilizar caracteres de escape en los separadores de ruta, por ejemplo: <code>\\Users\\myUser</code>.</p>
<p>Opcionalmente, un archivo de texto se puede dividir en líneas, generando un mensaje por línea, o un archivo binario dividido en fragmentos de búfer más pequeños; el tamaño del fragmento depende del sistema operativo, pero normalmente 64k (Linux/Mac) o 41k (Windows).</p>
<p>Cuando se divide en varios mensajes, cada mensaje tendrá una propiedad <code>parts</code> establecida, formando una secuencia de mensajes completa.</p>
<p>La codificación de los datos de entrada se puede especificar desde la lista de codificaciones si el formato de salida es una cadena.</p>
<p>Los errores deben detectarse y gestionarse mediante un nodo Captura (Catch).</p>
</script>

View File

@@ -0,0 +1,30 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
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/html" data-help-name="watch">
<p>Observa un directorio o archivo en busca de cambios.</p>
<p>Puedes ingresar una lista de directorios y/o archivos separados por comas. Tendrás que
poner comillas "..." alrededor de cualquier nombre que tenga espacios</p>
<p>En Windows debes utilizar barras invertidas dobles \\ en cualquier nombre de directorio.</p>
<p>El nombre completo del archivo que realmente cambió se coloca en <code>msg.payload</code> y <code>msg.filename</code>,
mientras que en <code>msg.topic</code> se devuelve una versión en texto de la lista de vigilancia.</p>
<p><code>msg.file</code> contiene solo el nombre corto del archivo que cambió.
<code>msg.type</code> tiene el tipo de cosa cambiada, generalmente <i>archivo</i> o <i>directorio</i>,
mientras que <code>msg.size</code> contiene el tamaño del archivo en bytes.</p>
<p>Por supuesto, en Linux, <i>todo</i> es un archivo y, por lo tanto, se puede observar...</p>
<p><b>Nota: </b>El directorio o archivo debe existir para poder ser observado. Si el archivo
o el directorio se elimina, es posible que ya no se vigile incluso si se vuelve a crear.</p>
</script>

View File

@@ -36,5 +36,5 @@
<p><b>Remarque</b> : Les options <i>"Intervalle entre les heures"</i> et <i>"à une heure précise"</i> utilisent le système cron standard.
Cela signifie que pour la première option, vous pouvez envoyer un message à intervalle régulier entre les heures voulues.
Si vous voulez envoyer un message toutes les minutes à partir de maintenant, utiliser l'option <i>"intervalle"</i>.</p>
<p><b>Remarque</b> : Pour inclure une nouvelle ligne dans une chaîne, vous devez utiliser un noeud de fonction pour créer la charge utile.</p>
<p><b>Remarque</b> : Pour inclure une nouvelle ligne dans une chaîne, vous devez utiliser soit un noeud de fonction soit le noeud template pour créer la charge utile.</p>
</script>

View File

@@ -11,11 +11,11 @@
"expand": "Développer"
},
"status": {
"connected": "connecté",
"not-connected": "pas connecté",
"disconnected": "déconnecté",
"connecting": "connexion",
"error": "erreur",
"connected": "Connecté",
"not-connected": "Pas connecté",
"disconnected": "Déconnecté",
"connecting": "Connexion",
"error": "Erreur",
"ok": "OK"
},
"notification": {
@@ -32,7 +32,7 @@
},
"inject": {
"inject": "Injecter",
"injectNow": "injecter maintenant",
"injectNow": "Injecter maintenant",
"repeat": "répéter = __repeat__",
"crontab": "crontab = __crontab__",
"stopped": "arrêté",
@@ -98,7 +98,7 @@
"catchUncaught": "catch : non capturé",
"label": {
"source": "Détecter les erreurs de",
"selectAll": "tout sélectionner",
"selectAll": "Tout sélectionner",
"uncaught": "Ignorer les erreurs gérées par les autres noeuds Catch"
},
"scope": {
@@ -112,7 +112,7 @@
"statusNodes": "statut : __number__",
"label": {
"source": "Signaler l'état de",
"sortByType": "trier par type"
"sortByType": "Trier par type"
},
"scope": {
"all": "tous les noeuds",
@@ -148,23 +148,23 @@
"deactivated": "Désactivé avec succès : __label__"
},
"sidebar": {
"label": "débogage",
"label": "Débogage",
"name": "Messages de débogage",
"filterAll": "tous les noeuds",
"filterSelected": "noeuds sélectionnés",
"filterCurrent": "flux actuel",
"filterAll": "Tous les noeuds",
"filterSelected": "Noeuds sélectionnés",
"filterCurrent": "Flux actuel",
"debugNodes": "noeuds de débogage",
"clearLog": "Effacer les messages",
"clearFilteredLog": "Effacer les messages filtrés",
"clearLog": "Tous les messages",
"clearFilteredLog": "Les messages filtrés",
"filterLog": "Filtrer les messages",
"openWindow": "Ouvrir dans une nouvelle fenêtre",
"copyPath": "Copier le chemin",
"copyPayload": "Copier la valeur",
"pinPath": "Épingler le chemin",
"selectAll": "tout sélectionner",
"selectNone": "ne rien sélectionner",
"all": "tout",
"filtered": "filtré"
"selectAll": "Tout sélectionner",
"selectNone": "Ne rien sélectionner",
"all": "Tout",
"filtered": "Filtrés"
},
"messageMenu": {
"collapseAll": "Réduire tous les chemins",
@@ -177,11 +177,11 @@
"linkIn": "Lien entrant",
"linkOut": "Lien sortant",
"linkCall": "Appel de lien",
"linkOutReturn": "retour de lien",
"linkOutReturn": "Retour de lien",
"outMode": "Mode",
"sendToAll": "Envoyer à tous les noeuds de liaison connectés",
"returnToCaller": "Retour au noeud de liaison appelant",
"timeout": "temps mort",
"timeout": "Temps mort",
"linkCallType": "Type de liaison",
"staticLinkCall": "Lien fixe",
"dynamicLinkCall": "Lien dynamique (msg.target)",
@@ -225,7 +225,7 @@
"command": "Commande",
"append": "Joindre",
"timeout": "Temps mort",
"timeoutplace": "facultatif",
"timeoutplace": "Facultatif",
"return": "Sortie",
"seconds": "secondes",
"stdout": "stdout",
@@ -234,7 +234,7 @@
"winHide": "Masquer la console"
},
"placeholder": {
"extraparams": "paramètres d'entrée supplémentaires"
"extraparams": "Paramètres d'entrée supplémentaires"
},
"opt": {
"exec": "lorsque la commande est terminée - mode exec",
@@ -319,7 +319,7 @@
"queuemsg": "Mettre en file d'attente les messages intermédiaires",
"dropmsg": "Supprimer les messages intermédiaires",
"sendmsg": "Envoyer les messages intermédiaires sur la 2ème sortie",
"allowrate": "autoriser msg.rate (en ms) à remplacer le débit",
"allowrate": "Autoriser msg.rate (en ms) à remplacer le débit",
"label": {
"delay": "retard",
"variable": "variable",
@@ -349,7 +349,7 @@
}
},
"errors": {
"too-many": "trop de messages en attente dans le noeud 'Delay'",
"too-many": "Trop de messages en attente dans le noeud 'Delay'",
"invalid-timeout": "Valeur de délai invalide",
"invalid-rate": "Valeur de taux invalide",
"invalid-rate-unit": "Valeur de débit invalide",
@@ -359,8 +359,8 @@
},
"trigger": {
"send": "Envoyer",
"then": "puis",
"then-send": "puis envoyer",
"then": "Puis",
"then-send": "Puis envoyer",
"output": {
"string": "la chaîne",
"number": "le nombre",
@@ -381,9 +381,9 @@
"m": "Minutes",
"h": "Heures"
},
"extend": " prolonger le délai si un nouveau message arrive",
"override": "remplacer le délai avec msg.delay",
"second": " envoyer un deuxième message à une sortie séparée",
"extend": " Prolonger le délai si un nouveau message arrive",
"override": "Remplacer le délai avec msg.delay",
"second": " Envoyer un deuxième message à une sortie séparée",
"label": {
"trigger": "déclencher",
"trigger-block": "déclencher et bloquer",
@@ -408,7 +408,7 @@
"mqtt": {
"label": {
"broker": "Serveur",
"example": "par exemple. localhost",
"example": "expl. localhost",
"output": "Sortie",
"qos": "QoS",
"retain": "Conserver",
@@ -438,7 +438,7 @@
"sessionExpiry": "Expiration de la session (secondes)",
"topicAlias": "Alias",
"payloadFormatIndicator": "Formater",
"payloadFormatIndicatorFalse": "octets non spécifiés (par défaut)",
"payloadFormatIndicatorFalse": "Octets non spécifiés (par défaut)",
"payloadFormatIndicatorTrue": "Charge utile encodée en UTF-8",
"protocolVersion": "Protocole",
"protocolVersion3": "MQTT V3.1 (hérité)",
@@ -493,8 +493,8 @@
"false": "faux",
"tip": "Conseil : laisser le sujet, le qos ou le contenu vide si vous souhaitez les définir via les propriétés du msg.",
"errors": {
"not-defined": "sujet non défini",
"missing-config": "configuration du courtier manquante",
"not-defined": "Sujet non défini",
"missing-config": "Configuration du courtier manquante",
"invalid-topic": "Sujet invalide spécifié",
"nonclean-missingclientid": "Aucun ID client défini, utilisation d'une session propre",
"invalid-json-string": "Chaîne JSON invalide",
@@ -514,7 +514,7 @@
"upload": "Accepter les téléchargements de fichiers ?",
"status": "Code d'état",
"headers": "En-têtes",
"other": "autre",
"other": "Autre",
"paytoqs": {
"ignore": "Ignorer",
"query": "Joindre aux paramètres de chaîne de requête",
@@ -625,7 +625,7 @@
"chars": "caractères",
"close": "Fermer",
"optional": "(facultatif)",
"reattach": "rattacher le délimiteur"
"reattach": "Rattacher le délimiteur"
},
"type": {
"listen": "Écoute sur",
@@ -633,8 +633,8 @@
"reply": "Répondre sur TCP"
},
"output": {
"stream": "flux de",
"single": "unique",
"stream": "Flux de",
"single": "Unique",
"buffer": "Tampon",
"string": "Chaîne",
"base64": "Chaîne en Base64"
@@ -657,15 +657,15 @@
"connections_plural": "__count__ connexions"
},
"errors": {
"connection-lost": "connexion perdue avec __host__:__port__",
"timeout": "délai d'expiration du port __port__ du socket fermé",
"cannot-listen": "impossible d'écouter sur le port __port__, erreur : __error__",
"error": "erreur : __error__",
"socket-error": "erreur de courtier depuis __host__:__port__",
"connection-lost": "Connexion perdue avec __host__:__port__",
"timeout": "Délai d'expiration du port __port__ du socket fermé",
"cannot-listen": "Impossible d'écouter sur le port __port__, erreur : __error__",
"error": "Erreur : __error__",
"socket-error": "Erreur de courtier depuis __host__:__port__",
"no-host": "Hôte et/ou port non défini",
"connect-timeout": "délai de connexion",
"connect-fail": "la connexion a échoué",
"bad-string": "échec de la conversion en chaîne",
"connect-timeout": "Délai de connexion",
"connect-fail": "La connexion a échoué",
"bad-string": "Échec de la conversion en chaîne",
"invalid-host": "Hôte invalide",
"invalid-port": "Port invalide"
}
@@ -722,7 +722,7 @@
},
"errors": {
"access-error": "Erreur d'accès UDP, vous aurez peut-être besoin d'un accès root pour les ports inférieurs à 1024",
"error": "erreur : __erreur__",
"error": "Erreur : __erreur__",
"bad-mcaddress": "Mauvaise adresse de multidiffusion",
"interface": "Doit être l'adresse IP de l'interface requise",
"ip-notset": "udp : adresse IP non définie",
@@ -730,7 +730,7 @@
"port-invalid": "udp : numéro de port non valide",
"alreadyused": "udp : port __port__ déjà utilisé",
"ifnotfound": "udp : interface __iface__ introuvable",
"invalid-group": "groupe de multidiffusion invalide"
"invalid-group": "Groupe de multidiffusion invalide"
}
},
"switch": {
@@ -738,15 +738,15 @@
"label": {
"property": "Propriété",
"rule": "règle",
"repair": "recréer des séquences du messages",
"value-rules": "règles de valeur",
"sequence-rules": "règles de séquence"
"repair": "Recréer des séquences du messages",
"value-rules": "Règles de valeur",
"sequence-rules": "Règles de séquence"
},
"previous": "valeur précédente",
"and": "et",
"checkall": "vérifier toutes les règles",
"stopfirst": "arrêter après la première concordance",
"ignorecase": "ignorer la casse",
"checkall": "Vérifier toutes les règles",
"stopfirst": "Arrêter après la première concordance",
"ignorecase": "Ignorer la casse",
"rules": {
"btwn": "est entre",
"cont": "contient",
@@ -767,7 +767,7 @@
},
"errors": {
"invalid-expr": "Expression JSONata non valide : __error__",
"too-many": "trop de messages en attente dans le noeud de commutation"
"too-many": "Trop de messages en attente dans le noeud de commutation"
}
},
"change": {
@@ -840,13 +840,13 @@
"entrée": "Entrée",
"skip-s": "Passer en premier",
"skip-e": "lignes",
"firstrow": "la première ligne contient les noms des colonnes",
"firstrow": "La première ligne contient les noms des colonnes",
"output": "Sortie",
"includerow": "inclure la ligne du nom de la colonne",
"includerow": "Inclure la ligne du nom de la colonne",
"newline": "Nouvelle ligne",
"usestrings": "analyser les valeurs numériques",
"include_empty_strings": "inclure les chaînes vides",
"include_null_values": "inclure les valeurs nulles"
"usestrings": "Analyser les valeurs numériques",
"include_empty_strings": "Inclure les chaînes vides",
"include_null_values": "Inclure les valeurs nulles"
},
"placeholder": {
"columns": "noms de colonnes séparés par des virgules"
@@ -936,8 +936,8 @@
},
"file": {
"label": {
"write": "écrire le fichier",
"read": "lire le fichier",
"write": "Écrire le fichier",
"read": "Lire le fichier",
"filename": "Nom du fichier",
"path": "chemin",
"action": "Action",
@@ -972,8 +972,8 @@
"appendedfile": "ajouté au fichier : __file__"
},
"encoding": {
"none": "par défaut",
"setbymsg": "défini par msg.encoding",
"none": "Par défaut",
"setbymsg": "Défini par msg.encoding",
"native": "Natif",
"unicode": "Unicode",
"japanese": "Japonais",
@@ -990,10 +990,10 @@
"errors": {
"nofilename": "Aucun nom de fichier spécifié",
"invaliddelete": "Attention : suppression non valide. Veuiller utiliser une option de suppression spécifique dans la boîte de dialogue de configuration.",
"deletefail": "échec de la suppression du fichier : __error__",
"writefail": "échec de l'écriture dans le fichier : __error__",
"appendfail": "échec de l'ajout au fichier : __error__",
"createfail": "échec de la création du fichier : __error__"
"deletefail": "Échec de la suppression du fichier : __error__",
"writefail": "Échec de l'écriture dans le fichier : __error__",
"appendfail": "Échec de l'ajout au fichier : __error__",
"createfail": "Échec de la création du fichier : __error__"
},
"tip": "Astuce : Le nom du fichier doit être un chemin absolu, sinon il sera relatif au répertoire de travail du processus Node-RED."
},
@@ -1018,9 +1018,9 @@
"reduce": "réduire la séquence",
"custom": "manuel"
},
"combine": "Combine each",
"combine": "Combiner chaque",
"completeMessage": "message complet",
"create": "créer",
"create": "Créer",
"type": {
"string": "une Chaîne",
"array": "un Tableau",
@@ -1028,13 +1028,13 @@
"object": "un Objet clé/valeur",
"merged": "un Objet fusionné"
},
"using": "en utilisant la valeur de",
"key": "comme la clé",
"using": "En utilisant la valeur du",
"key": "comme clé",
"joinedUsing": "joint en utilisant",
"send": "Envoyer le message :",
"afterCount": "Après un certain nombre de parties du message",
"count": "compter",
"subsequent": "et tous les messages suivants.",
"afterCount": "Après un nombre de parties du message",
"count": "nombre",
"subsequent": "Et tous les messages suivants.",
"afterTimeout": "Après un délai d'attente après le premier message",
"seconds": "secondes",
"complete": "Après un message avec la propriété <code>msg.complete</code> définie",
@@ -1068,10 +1068,10 @@
"order": "Sens",
"ascending": "croissant",
"descending": "descendant",
"as-number": "comme nombre",
"as-number": "Comme nombre",
"invalid-exp": "Expression JSONata invalide dans le noeud sort: __message__",
"too-many": "Trop de messages en attente dans le noeud sort",
"clear": "effacer le message en attente dans le noeud sort"
"clear": "Effacer le message en attente dans le noeud sort"
},
"batch": {
"batch": "Regrouper",
@@ -1084,21 +1084,21 @@
"count": {
"label": "Nombre de messages",
"overlap": "Chevauchement",
"count": "compter",
"count": "nombre",
"invalid": "Comptage et chevauchement invalides"
},
"interval": {
"label": "Intervalle",
"seconds": "secondes",
"empty": "envoyer un message vide lorsqu'aucun message n'arrive"
"empty": "Envoyer un message vide lorsqu'aucun message n'arrive"
},
"concat": {
"topics-label": "Sujets",
"topic": "sujet"
},
"too-many": "trop de messages en attente dans le noeud batch",
"unexpected": "mode inattendu",
"no-parts": "aucune propriété de pièces dans le message",
"too-many": "Trop de messages en attente dans le noeud batch",
"unexpected": "Mode inattendu",
"no-parts": "Aucune propriété de pièces dans le message",
"error": {
"invalid-count": "Compte invalide",
"invalid-overlap": "Recouvrement invalide",
@@ -1132,7 +1132,7 @@
"out": "par rapport à la dernière valeur de sortie valide"
},
"warn": {
"nonumber": "aucun numéro trouvé dans la charge utile"
"nonumber": "Aucun numéro trouvé dans la charge utile"
}
},
"global-config": {