Merge branch 'dev' into get-got

This commit is contained in:
Nick O'Leary
2021-06-09 09:59:23 +01:00
committed by GitHub
243 changed files with 43788 additions and 1320 deletions

View File

@@ -162,7 +162,62 @@
height += 16;
$("#node-input-property-container").editableList('height',height);
}
/** Retrieve editableList items (refactored for re-use in the form inject button)*/
function getProps(el, legacy) {
var result = {
props: []
}
el.each(function(i) {
var prop = $(this);
var p = {
p:prop.find(".node-input-prop-property-name").typedInput('value')
};
if (p.p) {
p.v = prop.find(".node-input-prop-property-value").typedInput('value');
p.vt = prop.find(".node-input-prop-property-value").typedInput('type');
if(legacy) {
if (p.p === "payload") { // save payload to old "legacy" property
result.payloadType = p.vt;
result.payload = p.v;
delete p.v;
delete p.vt;
} else if (p.p === "topic" && p.vt === "str") {
result.topic = p.v;
delete p.v;
}
}
result.props.push(p);
}
});
return result;
}
/** Perform inject, optionally sending a custom msg (refactored for re-use in the form inject button)*/
function doInject(node, customMsg) {
var label = node._def.label.call(node);
if (label.length > 30) {
label = label.substring(0, 50) + "...";
}
label = label.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
$.ajax({
url: "inject/" + node.id,
type: "POST",
data: customMsg,
success: function (resp) {
RED.notify(node._("inject.success", { label: label }), { type: "success", id: "inject", timeout: 2000 });
},
error: function (jqXHR, textStatus, errorThrown) {
if (jqXHR.status == 404) {
RED.notify(node._("common.notification.error", { message: node._("common.notification.errors.not-deployed") }), "error");
} else if (jqXHR.status == 500) {
RED.notify(node._("common.notification.error", { message: node._("inject.errors.failed") }), "error");
} else if (jqXHR.status == 0) {
RED.notify(node._("common.notification.error", { message: node._("common.notification.errors.no-response") }), "error");
} else {
RED.notify(node._("common.notification.error", { message: node._("common.notification.errors.unexpected", { status: jqXHR.status, message: textStatus }) }), "error");
}
}
});
}
RED.nodes.registerType('inject',{  
category: 'common',
color:"#a6bbcf",
@@ -214,7 +269,7 @@
for (var i=0,l=props.length; i<l; i++) {
if (i > 0) lab += "\n";
if (i === 5) {
lab += " + "+(props.length-4);
lab += "... +"+(props.length-5);
break;
}
lab += props[i].p+": ";
@@ -283,14 +338,15 @@
return this.name?"node_label_italic":"";
},
oneditprepare: function() {
if (this.payloadType == null) {
if (this.payload == "") {
this.payloadType = "date";
var node = this;
if (node.payloadType == null) {
if (node.payload == "") {
node.payloadType = "date";
} else {
this.payloadType = "str";
node.payloadType = "str";
}
} else if (this.payloadType === 'string' || this.payloadType === 'none') {
this.payloadType = "str";
} else if (node.payloadType === 'string' || node.payloadType === 'none') {
node.payloadType = "str";
}
$("#inject-time-type-select").on("change", function() {
@@ -345,17 +401,17 @@
});
var repeattype = "none";
if (this.repeat != "" && this.repeat != 0) {
if (node.repeat != "" && node.repeat != 0) {
repeattype = "interval";
var r = "s";
var c = this.repeat;
if (this.repeat % 60 === 0) { r = "m"; c = c/60; }
if (this.repeat % 1440 === 0) { r = "h"; c = c/60; }
var c = node.repeat;
if (node.repeat % 60 === 0) { r = "m"; c = c/60; }
if (node.repeat % 1440 === 0) { r = "h"; c = c/60; }
$("#inject-time-interval-count").val(c);
$("#inject-time-interval-units").val(r);
$("#inject-time-interval-days").prop("disabled","disabled");
} else if (this.crontab) {
var cronparts = this.crontab.split(" ");
} else if (node.crontab) {
var cronparts = node.crontab.split(" ");
var days = cronparts[4];
if (!isNaN(cronparts[0]) && !isNaN(cronparts[1])) {
repeattype = "time";
@@ -431,7 +487,24 @@
/* */
$('#node-input-property-container').css('min-height','120px').css('min-width','450px').editableList({
var eList = $('#node-input-property-container').css('min-height','120px').css('min-width','450px');
eList.editableList({
buttons: [
{
id: "node-inject-test-inject-button",
label: node._("inject.injectNow"),
click: function(e) {
var items = eList.editableList('items');
var result = getProps(items);
var m = {__user_inject_props__: []};
if (result && result.props && result.props.length) {
m.__user_inject_props__ = result.props;
}
doInject(node, m);
}
}
],
addItem: function(container,i,opt) {
var prop = opt;
if (!prop.hasOwnProperty('p')) {
@@ -455,7 +528,7 @@
var propertyValue = $('<input/>',{class:"node-input-prop-property-value",type:"text"})
.css("width","calc(70% - 30px)")
.appendTo(row)
.typedInput({default:'str',types:['msg','flow','global','str','num','bool','json','bin','date','jsonata','env']});
.typedInput({default:'str',types:['flow','global','str','num','bool','json','bin','date','jsonata','env','msg']});
propertyName.typedInput('value',prop.p);
@@ -465,33 +538,38 @@
removable: true,
sortable: true
});
$('#node-inject-test-inject-button').css("float", "right").css("margin-right", "unset");
if (!this.props) {
if (RED.nodes.subflow(node.z)) {
$('#node-inject-test-inject-button').attr("disabled",true);
}
if (!node.props) {
var payload = {
p:'payload',
v: this.payload ? this.payload : '',
vt:this.payloadType ? this.payloadType : 'date'
v: node.payload ? node.payload : '',
vt:node.payloadType ? node.payloadType : 'date'
};
var topic = {
p:'topic',
v: this.topic ? this.topic : '',
v: node.topic ? node.topic : '',
vt:'string'
}
this.props = [payload,topic];
node.props = [payload,topic];
}
for (var i=0; i<this.props.length; i++) {
var prop = this.props[i];
for (var i=0; i<node.props.length; i++) {
var prop = node.props[i];
var newProp = { p: prop.p, v: prop.v, vt: prop.vt };
if (newProp.v === undefined) {
if (prop.p === 'payload') {
newProp.v = this.payload ? this.payload : '';
newProp.vt = this.payloadType ? this.payloadType : 'date';
newProp.v = node.payload ? node.payload : '';
newProp.vt = node.payloadType ? node.payloadType : 'date';
} else if (prop.p === 'topic' && prop.vt === "str") {
newProp.v = this.topic ? this.topic : '';
newProp.v = node.topic ? node.topic : '';
}
}
$("#node-input-property-container").editableList('addItem',newProp);
eList.editableList('addItem',newProp);
}
$("#inject-time-type-select").trigger("change");
@@ -589,67 +667,26 @@
$("#node-input-repeat").val(repeat);
$("#node-input-crontab").val(crontab);
/* Gather the injected properties of the msg object */
var props = $("#node-input-property-container").editableList('items');
var node = this;
node.props= [];
delete node.payloadType;
delete node.payload;
node.topic = "";
props.each(function(i) {
var prop = $(this);
var p = {
p:prop.find(".node-input-prop-property-name").typedInput('value')
};
if (p.p) {
p.v = prop.find(".node-input-prop-property-value").typedInput('value');
p.vt = prop.find(".node-input-prop-property-value").typedInput('type');
if (p.p === "payload") { // save payload to old "legacy" property
node.payloadType = p.vt;
node.payload = p.v;
delete p.v;
delete p.vt;
} else if (p.p === "topic" && p.vt === "str") {
node.topic = p.v;
delete p.v;
}
node.props.push(p);
}
});
/* Gather the properties */
var items = $("#node-input-property-container").editableList('items');
delete this.payloadType;
delete this.payload;
this.topic = "";
var result = getProps(items, true);
this.props = result.props;
if(result.payloadType) { this.payloadType = result.payloadType; };
if(result.payload) { this.payload = result.payload; };
if(result.topic) { this.topic = result.topic; };
},
button: {
enabled: function() {
return !this.changed
},
onclick: function() {
onclick: function () {
if (this.changed) {
return RED.notify(RED._("notification.warning", {message:RED._("notification.warnings.undeployedChanges")}),"warning");
return RED.notify(RED._("notification.warning", { message: RED._("notification.warnings.undeployedChanges") }), "warning");
}
var label = this._def.label.call(this);
if (label.length > 30) {
label = label.substring(0,50)+"...";
}
label = label.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");
var node = this;
$.ajax({
url: "inject/"+this.id,
type:"POST",
success: function(resp) {
RED.notify(node._("inject.success",{label:label}),{type:"success",id:"inject"});
},
error: function(jqXHR,textStatus,errorThrown) {
if (jqXHR.status == 404) {
RED.notify(node._("common.notification.error",{message:node._("common.notification.errors.not-deployed")}),"error");
} else if (jqXHR.status == 500) {
RED.notify(node._("common.notification.error",{message:node._("inject.errors.failed")}),"error");
} else if (jqXHR.status == 0) {
RED.notify(node._("common.notification.error",{message:node._("common.notification.errors.no-response")}),"error");
} else {
RED.notify(node._("common.notification.error",{message:node._("common.notification.errors.unexpected",{status:jqXHR.status,message:textStatus})}),"error");
}
}
});
doInject(this);
}
},
oneditresize: resizeDialog

View File

@@ -16,7 +16,7 @@
module.exports = function(RED) {
"use strict";
var cron = require("cron");
const {scheduleTask} = require("cronosjs");
function InjectNode(n) {
RED.nodes.createNode(this,n);
@@ -85,7 +85,7 @@ module.exports = function(RED) {
if (RED.settings.verbose) {
this.log(RED._("inject.crontab", this));
}
this.cronjob = new cron.CronJob(this.crontab, function() { node.emit("input", {}); }, null, true);
this.cronjob = scheduleTask(this.crontab,() => { node.emit("input", {})});
}
};
@@ -100,8 +100,12 @@ module.exports = function(RED) {
this.on("input", function(msg, send, done) {
var errors = [];
this.props.forEach(p => {
var props = this.props;
if(msg.__user_inject_props__ && Array.isArray(msg.__user_inject_props__)) {
props = msg.__user_inject_props__;
}
delete msg.__user_inject_props__;
props.forEach(p => {
var property = p.p;
var value = p.v ? p.v : '';
var valueType = p.vt ? p.vt : 'str';
@@ -156,7 +160,11 @@ module.exports = function(RED) {
var node = RED.nodes.getNode(req.params.id);
if (node != null) {
try {
node.receive();
if (req.body && req.body.__user_inject_props__) {
node.receive(req.body);
} else {
node.receive();
}
res.sendStatus(200);
} catch(err) {
res.sendStatus(500);

View File

@@ -129,9 +129,9 @@
RED.history.push(historyEvent);
RED.view.redraw();
if (xhr.status == 200) {
RED.notify(node._("debug.notification.activated",{label:label}),"success");
RED.notify(node._("debug.notification.activated",{label:label}),{type: "success", timeout: 2000});
} else if (xhr.status == 201) {
RED.notify(node._("debug.notification.deactivated",{label:label}),"success");
RED.notify(node._("debug.notification.deactivated",{label:label}),{type: "success", timeout: 2000});
}
});
}

View File

@@ -2,7 +2,8 @@ module.exports = function(RED) {
"use strict";
var util = require("util");
var events = require("events");
//var path = require("path");
const fs = require("fs-extra");
const path = require("path");
var debuglength = RED.settings.debugMaxLength || 1000;
var useColors = RED.settings.debugUseColors || false;
util.inspect.styles.boolean = "red";
@@ -249,11 +250,34 @@ module.exports = function(RED) {
}
});
let cachedDebugView;
RED.httpAdmin.get("/debug/view/view.html", function(req,res) {
if (!cachedDebugView) {
fs.readFile(path.join(__dirname,"lib","debug","view.html")).then(data => {
let customStyles = "";
try {
let customStyleList = RED.settings.editorTheme.page._.css || [];
customStyleList.forEach(style => {
customStyles += `<link rel="stylesheet" href="../../${style}">\n`
})
} catch(err) {}
cachedDebugView = data.toString().replace("<!-- INSERT-THEME-CSS -->",customStyles)
res.set('Content-Type', 'text/html');
res.send(cachedDebugView).end();
}).catch(err => {
res.sendStatus(404);
})
} else {
res.send(cachedDebugView).end();
}
});
// As debug/view/debug-utils.js is loaded via <script> tag, it won't get
// the auth header attached. So do not use RED.auth.needsPermission here.
RED.httpAdmin.get("/debug/view/*",function(req,res) {
var options = {
root: __dirname + '/lib/debug/',
root: path.join(__dirname,"lib","debug"),
dotfiles: 'deny'
};
res.sendFile(req.params[0], options);

View File

@@ -2,6 +2,7 @@
<head>
<link rel="stylesheet" href="../../red/style.min.css">
<link rel="stylesheet" href="../../vendor/font-awesome/css/font-awesome.min.css">
<!-- INSERT-THEME-CSS -->
<title>Node-RED Debug Tools</title>
</head>
<body class="red-ui-editor red-ui-debug-window">

View File

@@ -74,21 +74,21 @@
<div id="func-tab-init" style="display:none">
<div class="form-row node-text-editor-row" style="position:relative">
<div style="height: 250px; min-height:150px;" class="node-text-editor" id="node-input-init-editor" ></div>
<div style="position: absolute; right:0; bottom: calc(100% - 20px);"><button id="node-init-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div>
<div style="position: absolute; right:0; bottom: calc(100% - 20px); z-Index: 10;"><button id="node-init-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div>
</div>
</div>
<div id="func-tab-body" style="display:none">
<div class="form-row node-text-editor-row" style="position:relative">
<div style="height: 220px; min-height:150px;" class="node-text-editor" id="node-input-func-editor" ></div>
<div style="position: absolute; right:0; bottom: calc(100% - 20px);"><button id="node-function-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div>
<div style="position: absolute; right:0; bottom: calc(100% - 20px); z-Index: 10;"><button id="node-function-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div>
</div>
</div>
<div id="func-tab-finalize" style="display:none">
<div class="form-row node-text-editor-row" style="position:relative">
<div style="height: 250px; min-height:150px;" class="node-text-editor" id="node-input-finalize-editor" ></div>
<div style="position: absolute; right:0; bottom: calc(100% - 20px);"><button id="node-finalize-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div>
<div style="position: absolute; right:0; bottom: calc(100% - 20px); z-Index: 10;"><button id="node-finalize-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div>
</div>
</div>
@@ -312,6 +312,30 @@
}
}
function getLibsList() {
var _libs = [];
if (RED.settings.functionExternalModules === true) {
var libs = $("#node-input-libs-container").editableList("items");
libs.each(function(i) {
var item = $(this);
var v = item.find(".node-input-libs-var").val();
var n = item.find(".node-input-libs-val").typedInput("type");
if (n === "_custom_") {
n = item.find(".node-input-libs-val").val();
}
if ((!v || (v === "")) ||
(!n || (n === ""))) {
return;
}
_libs.push({
var: v,
module: n
});
});
}
return _libs;
}
RED.nodes.registerType('function',{
color:"#fdd0a2",
@@ -360,6 +384,13 @@
onchange: function(tab) {
$("#func-tabs-content").children().hide();
$("#" + tab.id).show();
let editor = $("#" + tab.id).find('.monaco-editor').first();
if(editor.length) {
if(that.editor.nodered && that.editor.type == "monaco") {
that.editor.nodered.refreshModuleLibs(getLibsList());
}
RED.tray.resize();
}
}
});
tabs.addTab({
@@ -393,7 +424,7 @@
}
});
var buildEditor = function(id, value, defaultValue) {
var buildEditor = function(id, value, defaultValue, extraLibs) {
var editor = RED.editor.createEditor({
id: id,
mode: 'ace/mode/nrjavascript',
@@ -411,7 +442,8 @@
clearTimeout: true,
setInterval: true,
clearInterval: true
}
},
extraLibs: extraLibs
});
if (defaultValue && value === "") {
editor.moveCursorTo(defaultValue.split("\n").length - 1, 0);
@@ -419,7 +451,7 @@
return editor;
}
this.initEditor = buildEditor('node-input-init-editor',$("#node-input-initialize").val(),RED._("node-red:function.text.initialize"))
this.editor = buildEditor('node-input-func-editor',$("#node-input-func").val())
this.editor = buildEditor('node-input-func-editor',$("#node-input-func").val(), undefined, that.libs || [])
this.finalizeEditor = buildEditor('node-input-finalize-editor',$("#node-input-finalize").val(),RED._("node-red:function.text.finalize"))
RED.library.create({
@@ -523,28 +555,7 @@
$("#node-input-noerr").val(noerr);
this.noerr = noerr;
if (RED.settings.functionExternalModules === true) {
var libs = $("#node-input-libs-container").editableList("items");
node.libs = [];
libs.each(function(i) {
var item = $(this);
var v = item.find(".node-input-libs-var").val();
var n = item.find(".node-input-libs-val").typedInput("type");
if (n === "_custom_") {
n = item.find(".node-input-libs-val").val();
}
if ((!v || (v === "")) ||
(!n || (n === ""))) {
return;
}
node.libs.push({
var: v,
module: n
});
});
} else {
node.libs = [];
}
node.libs = getLibsList();
},
oneditcancel: function() {
var node = this;

View File

@@ -19,6 +19,8 @@ module.exports = function(RED) {
var util = require("util");
var vm = require("vm");
var acorn = require("acorn");
var acornWalk = require("acorn-walk");
function sendResults(node,send,_msgid,msgs,cloneFirstMessage) {
if (msgs == null) {
@@ -102,14 +104,7 @@ module.exports = function(RED) {
throw new Error(RED._("function.error.externalModuleNotAllowed"));
}
var handleNodeDoneCall = true;
// Check to see if the Function appears to call `node.done()`. If so,
// we will assume it is well written and does actually call node.done().
// Otherwise, we will call node.done() after the function returns regardless.
if (/node\.done\s*\(\s*\)/.test(node.func)) {
handleNodeDoneCall = false;
}
var functionText = "var results = null;"+
"results = (async function(msg,__send__,__done__){ "+
@@ -130,6 +125,26 @@ module.exports = function(RED) {
"};\n"+
node.func+"\n"+
"})(msg,__send__,__done__);";
var handleNodeDoneCall = true;
// Check to see if the Function appears to call `node.done()`. If so,
// we will assume it is well written and does actually call node.done().
// Otherwise, we will call node.done() after the function returns regardless.
if (/node\.done\s*\(\s*\)/.test(functionText)) {
// We have spotted the code contains `node.done`. It could be in a comment
// so need to do the extra work to parse the AST and examine it properly.
acornWalk.simple(acorn.parse(functionText,{ecmaVersion: "latest"} ), {
CallExpression(astNode) {
if (astNode.callee && astNode.callee.object) {
if (astNode.callee.object.name === "node" && astNode.callee.property.name === "done") {
handleNodeDoneCall = false;
}
}
}
})
}
var finScript = null;
var finOpt = null;
node.topic = n.topic;
@@ -302,7 +317,7 @@ module.exports = function(RED) {
}
});
if (moduleErrors) {
throw new Error("Function node failed to load external modules");
throw new Error(RED._("function.error.externalModuleLoadError"));
}
}
@@ -366,7 +381,8 @@ module.exports = function(RED) {
__node__.error("Cannot send from close function");
}
};
`+node.fin +`})();`;
`+node.fin +`
})();`;
finOpt = createVMOpt(node, " cleanup");
finScript = new vm.Script(finText, finOpt);
}
@@ -489,4 +505,3 @@ module.exports = function(RED) {
});
RED.library.register("functions");
};

View File

@@ -217,6 +217,10 @@ module.exports = function(RED) {
function applyRules(node, msg, property,state,done) {
if (!state) {
if (node.rules.length === 0) {
done(undefined, []);
return;
}
state = {
currentRule: 0,
elseflag: true,

View File

@@ -45,9 +45,9 @@
</div>
<div id="random-details" class="form-row">
<label for="node-input-randomFirst"><i class="fa fa-clock-o"></i> <span data-i18n="delay.between"></span></label>
<input type="text" id="node-input-randomFirst" placeholder="" style="text-align:end; width:30px !important">
<span data-i18n="delay.and"></span>
<input type="text" id="node-input-randomLast" placeholder="" style="text-align:end; width:30px !important">
<input type="text" id="node-input-randomFirst" placeholder="" style="text-align:end; width:50px !important">
&nbsp;<span data-i18n="delay.and"></span>&nbsp;
<input type="text" id="node-input-randomLast" placeholder="" style="text-align:end; width:50px !important">
<select id="node-input-randomUnits" style="width:140px !important">
<option value="milliseconds" data-i18n="delay.milisecs"></option>
<option value="seconds" data-i18n="delay.secs"></option>
@@ -78,8 +78,11 @@
<option value="day" data-i18n="delay.label.units.day.singular"></option>
</select>
</div>
<div class="form-row" id="rate-override">
<label></label><input style="width:30px; vertical-align:baseline;" type="checkbox" id="node-input-allowrate"><label style="width: 250px;" for="node-input-allowrate" data-i18n="delay.allowrate"></label>
</div>
<div class="form-row" id="rate-details-drop">
<label></label><input style="width: 30px;" type="checkbox" id="node-input-drop"><label style="width: 250px;" for="node-input-drop" data-i18n="delay.dropmsg"></label>
<label></label><input style="width:30px;; vertical-align:baseline;" type="checkbox" id="node-input-drop"><label style="width: 250px;" for="node-input-drop" data-i18n="delay.dropmsg"></label>
</div>
<div class="form-row" id="rate-details-per-topic">
<label></label>
@@ -110,7 +113,8 @@
randomFirst: {value:"1", required:true, validate:function(v) { return RED.validators.number(v) && (v >= 0); }},
randomLast: {value:"5", required:true, validate:function(v) { return RED.validators.number(v) && (v >= 0); }},
randomUnits: {value: "seconds"},
drop: {value:false}
drop: {value:false},
allowrate: {value:false}
},
inputs:1,
outputs:1,

View File

@@ -20,6 +20,20 @@ module.exports = function(RED) {
var MILLIS_TO_NANOS = 1000000;
var SECONDS_TO_NANOS = 1000000000;
var _maxKeptMsgsCount;
function maxKeptMsgsCount(node) {
if (_maxKeptMsgsCount === undefined) {
var name = "nodeMessageBufferMaxLength";
if (RED.settings.hasOwnProperty(name)) {
_maxKeptMsgsCount = RED.settings[name];
}
else {
_maxKeptMsgsCount = 0;
}
}
return _maxKeptMsgsCount;
}
function DelayNode(n) {
RED.nodes.createNode(this,n);
@@ -78,6 +92,9 @@ module.exports = function(RED) {
this.randomID = -1;
this.lastSent = null;
this.drop = n.drop;
this.droppedMsgs = 0;
this.allowrate = n.allowrate|| false;
this.fixedrate = this.rate;
var node = this;
function ourTimeout(handler, delay, clearHandler) {
@@ -88,6 +105,19 @@ module.exports = function(RED) {
};
}
var sendMsgFromBuffer = function() {
if (node.buffer.length === 0) {
clearInterval(node.intervalID);
node.intervalID = -1;
}
if (node.buffer.length > 0) {
const msgInfo = node.buffer.shift();
msgInfo.send(msgInfo.msg);
msgInfo.done();
}
node.reportDepth();
}
var clearDelayList = function(s) {
for (var i=0; i<node.idList.length; i++ ) { node.idList[i].clear(); }
node.idList = [];
@@ -112,21 +142,28 @@ module.exports = function(RED) {
}
}
var loggerId = setInterval(function () {
if (node.droppedMsgs !== 0) {
node.debug("node.droppedMsgs = " + node.droppedMsgs);
node.droppedMsgs = 0;
}
}, 15 * 1000);
node.on("close", function() { clearInterval(loggerId); });
if (node.pauseType === "delay") {
node.on("input", function(msg, send, done) {
var id = ourTimeout(function() {
node.idList.splice(node.idList.indexOf(id),1);
if (node.idList.length === 0) { node.status({}); }
send(msg);
done();
}, node.timeout, () => done());
if (Object.keys(msg).length === 2 && msg.hasOwnProperty("flush")) { id.clear(); }
else { node.idList.push(id); }
if (msg.hasOwnProperty("reset")) { clearDelayList(true); }
if (msg.hasOwnProperty("flush")) { flushDelayList(); done(); }
else {
var id = ourTimeout(function() {
node.idList.splice(node.idList.indexOf(id),1);
if (node.idList.length === 0) { node.status({}); }
send(msg);
done();
}, node.timeout, () => done());
node.idList.push(id);
if ((node.timeout > 1000) && (node.idList.length !== 0)) {
node.status({fill:"blue",shape:"dot",text:" "});
}
if (msg.hasOwnProperty("reset")) { clearDelayList(true); }
if ((node.timeout > 1000) && (node.idList.length !== 0)) {
node.status({fill:"blue",shape:"dot",text:" "});
}
});
node.on("close", function() { clearDelayList(); });
@@ -145,11 +182,11 @@ module.exports = function(RED) {
done();
}, delayvar, () => done());
node.idList.push(id);
if (msg.hasOwnProperty("reset")) { clearDelayList(true); }
if (msg.hasOwnProperty("flush")) { flushDelayList(); done(); }
if ((delayvar >= 0) && (node.idList.length !== 0)) {
node.status({fill:"blue",shape:"dot",text:delayvar/1000+"s"});
}
if (msg.hasOwnProperty("reset")) { clearDelayList(true); }
if (msg.hasOwnProperty("flush")) { flushDelayList(); }
});
node.on("close", function() { clearDelayList(); });
}
@@ -162,32 +199,37 @@ module.exports = function(RED) {
}
delete node.lastSent;
node.buffer = [];
node.rate = node.fixedrate;
node.status({text:"reset"});
done();
return;
}
if (!node.drop) {
var m = RED.util.cloneMessage(msg);
delete m.flush;
if (node.intervalID !== -1) {
node.buffer.push({msg: m, send: send, done: done});
node.reportDepth();
if (node.allowrate && msg.hasOwnProperty("rate") && !isNaN(parseFloat(msg.rate)) && node.rate !== msg.rate) {
node.rate = msg.rate;
clearInterval(node.intervalID);
node.intervalID = setInterval(sendMsgFromBuffer, node.rate);
}
var max_msgs = maxKeptMsgsCount(node);
if ((max_msgs > 0) && (node.buffer.length >= max_msgs)) {
node.buffer = [];
node.error(RED._("delay.errors.too-many"), msg);
} else {
node.buffer.push({msg: m, send: send, done: done});
node.reportDepth();
}
}
else {
if (node.allowrate && msg.hasOwnProperty("rate") && !isNaN(parseFloat(msg.rate))) {
node.rate = msg.rate;
}
send(m);
node.reportDepth();
node.intervalID = setInterval(function() {
if (node.buffer.length === 0) {
clearInterval(node.intervalID);
node.intervalID = -1;
}
if (node.buffer.length > 0) {
const msgInfo = node.buffer.shift();
msgInfo.send(msgInfo.msg);
msgInfo.done();
}
node.reportDepth();
}, node.rate);
node.intervalID = setInterval(sendMsgFromBuffer, node.rate);
done();
}
if (msg.hasOwnProperty("flush")) {
@@ -201,17 +243,40 @@ module.exports = function(RED) {
}
}
else {
var timeSinceLast;
if (node.lastSent) {
timeSinceLast = process.hrtime(node.lastSent);
}
if (!node.lastSent) { // ensuring that we always send the first message
node.lastSent = process.hrtime();
send(msg);
}
else if ( ( (timeSinceLast[0] * SECONDS_TO_NANOS) + timeSinceLast[1] ) > (node.rate * MILLIS_TO_NANOS) ) {
node.lastSent = process.hrtime();
send(msg);
if (maxKeptMsgsCount(node) > 0) {
if (node.intervalID === -1) {
node.send(msg);
node.intervalID = setInterval(sendMsgFromBuffer, node.rate);
} else {
if (node.allowrate && msg.hasOwnProperty("rate") && !isNaN(parseFloat(msg.rate)) && node.rate !== msg.rate) {
node.rate = msg.rate;
clearInterval(node.intervalID);
node.intervalID = setInterval(sendMsgFromBuffer, node.rate);
}
if (node.buffer.length < _maxKeptMsgsCount) {
var m = RED.util.cloneMessage(msg);
node.buffer.push({msg: m, send: send, done: done});
} else {
node.trace("dropped due to buffer overflow. msg._msgid = " + msg._msgid);
node.droppedMsgs++;
}
}
} else {
if (node.allowrate && msg.hasOwnProperty("rate") && !isNaN(parseFloat(msg.rate))) {
node.rate = msg.rate;
}
var timeSinceLast;
if (node.lastSent) {
timeSinceLast = process.hrtime(node.lastSent);
}
if (!node.lastSent) { // ensuring that we always send the first message
node.lastSent = process.hrtime();
send(msg);
}
else if ( ( (timeSinceLast[0] * SECONDS_TO_NANOS) + timeSinceLast[1] ) > (node.rate * MILLIS_TO_NANOS) ) {
node.lastSent = process.hrtime();
send(msg);
}
}
done();
}
@@ -245,6 +310,11 @@ module.exports = function(RED) {
var hit;
node.on("input", function(msg, send, done) {
if (node.allowrate && msg.hasOwnProperty("rate") && !isNaN(parseFloat(msg.rate)) && node.rate !== msg.rate) {
node.rate = msg.rate;
clearInterval(node.intervalID);
node.intervalID = setInterval(sendMsgFromBuffer, node.rate);
}
if (!msg.hasOwnProperty("topic")) { msg.topic = "_none_"; }
hit = false;
for (var b in node.buffer) { // check if already in queue
@@ -265,6 +335,7 @@ module.exports = function(RED) {
msgInfo.done();
}
node.buffer = [];
node.rate = node.fixedrate;
node.status({text:"reset"});
done();
}
@@ -297,12 +368,13 @@ module.exports = function(RED) {
node.status({});
done();
}, wait, () => done());
node.idList.push(id);
if (Object.keys(msg).length === 2 && msg.hasOwnProperty("flush")) { id.clear(); }
else { node.idList.push(id); }
if (msg.hasOwnProperty("reset")) { clearDelayList(true); }
if (msg.hasOwnProperty("flush")) { flushDelayList(); done(); }
if ((node.timeout >= 1000) && (node.idList.length !== 0)) {
node.status({fill:"blue",shape:"dot",text:parseInt(wait/10)/100+"s"});
}
if (msg.hasOwnProperty("reset")) { clearDelayList(true); }
if (msg.hasOwnProperty("flush")) { flushDelayList(); }
});
node.on("close", function() { clearDelayList(); });
}

View File

@@ -67,6 +67,10 @@
<label style="width: 120px;" for="node-config-input-servername"><i class="fa fa-server"></i> <span data-i18n="tls.label.servername"></span></label>
<input style="width: calc(100% - 170px);" type="text" id="node-config-input-servername" data-i18n="[placeholder]tls.placeholder.servername">
</div>
<div class="form-row">
<label style="width: 120px;" for="node-config-input-alpnprotocol"><i class="fa fa-cogs"></i> <span data-i18n="tls.label.alpnprotocol"></span></label>
<input style="width: calc(100% - 170px);" type="text" id="node-config-input-alpnprotocol" data-i18n="[placeholder]tls.placeholder.alpnprotocol">
</div>
<hr>
<div class="form-row">
<label style="width: 120px;" for="node-config-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
@@ -98,7 +102,8 @@
keyname: {value:""},
caname: {value:""},
servername: {value:""},
verifyservercert: {value: true}
verifyservercert: {value: true},
alpnprotocol: {value: ""}
},
credentials: {
certdata: {type:"text"},

View File

@@ -26,6 +26,7 @@ module.exports = function(RED) {
var keyPath = n.key.trim();
var caPath = n.ca.trim();
this.servername = (n.servername||"").trim();
this.alpnprotocol = (n.alpnprotocol||"").trim();
if ((certPath.length > 0) || (keyPath.length > 0) || (caPath.length > 0)) {
@@ -106,6 +107,9 @@ module.exports = function(RED) {
if (this.servername) {
opts.servername = this.servername;
}
if (this.alpnprotocol) {
opts.ALPNProtocols = [this.alpnprotocol];
}
opts.rejectUnauthorized = this.verifyservercert;
}
return opts;

View File

@@ -186,8 +186,8 @@
<input type="text" id="node-config-input-port" data-i18n="[placeholder]mqtt.label.port" style="width:55px">
</div>
<div class="form-row" style="height: 34px;">
<input type="checkbox" id="node-config-input-usetls" style="height: 34px; margin: 0 0 0 104px; display: inline-block; width: auto; vertical-align: top;">
<label for="node-config-input-usetls" style="width: 80px; line-height: 34px;"><span data-i18n="mqtt.label.use-tls"></span></label>
<input type="checkbox" id="node-config-input-usetls" style="height: 34px; margin: 0 5px 0 104px; display: inline-block; width: auto; vertical-align: top;">
<label for="node-config-input-usetls" style="width: 100px; line-height: 34px;"><span data-i18n="mqtt.label.use-tls"></span></label>
<span id="node-config-row-tls" class="hide"><input style="width: 320px;" type="text" id="node-config-input-tls"></span>
</div>
<div class="form-row">

View File

@@ -314,7 +314,7 @@ module.exports = function(RED) {
}
// Build options for passing to the MQTT.js API
this.options.clientId = this.clientid || 'mqtt_' + (1+Math.random()*4294967295).toString(16);
this.options.clientId = this.clientid || 'mqtt_' + RED.util.generateId();
this.options.username = this.username;
this.options.password = this.password;
this.options.keepalive = this.keepalive;
@@ -400,7 +400,15 @@ module.exports = function(RED) {
}
if (Object.keys(node.users).length === 0) {
if (node.client && node.client.connected) {
return node.client.end(done);
// Send close message
if (node.closeMessage) {
node.publish(node.closeMessage,function(err) {
node.client.end(done);
});
} else {
node.client.end(done);
}
return;
} else {
node.client.end();
return done();
@@ -639,10 +647,6 @@ module.exports = function(RED) {
this.on('close', function(done) {
this.closing = true;
if (this.connected) {
// Send close message
if (node.closeMessage) {
node.publish(node.closeMessage);
}
this.client.once('close', function() {
done();
});
@@ -873,4 +877,4 @@ module.exports = function(RED) {
}
}
RED.nodes.registerType("mqtt out",MQTTOutNode);
};
};

View File

@@ -46,7 +46,9 @@ module.exports = function(RED) {
isText = true;
} else if (parsedType.type !== "application") {
isText = false;
} else if ((parsedType.subtype !== "octet-stream") && (parsedType.subtype !== "cbor")) {
} else if ((parsedType.subtype !== "octet-stream")
&& (parsedType.subtype !== "cbor")
&& (parsedType.subtype !== "x-protobuf")) {
checkUTF = true;
} else {
// application/octet-stream or application/cbor

View File

@@ -62,14 +62,14 @@ module.exports = function(RED) {
if (process.env.HTTP_PROXY) { prox = process.env.HTTP_PROXY; }
if (process.env.no_proxy) { noprox = process.env.no_proxy.split(","); }
if (process.env.NO_PROXY) { noprox = process.env.NO_PROXY.split(","); }
var noproxy = false;
if (noprox) {
for (var i in noprox) {
if (node.path.indexOf(noprox[i].trim()) !== -1) { noproxy=true; }
}
}
var agent = undefined;
if (prox && !noproxy) {
agent = new HttpsProxyAgent(prox);
@@ -92,7 +92,7 @@ module.exports = function(RED) {
}
function handleConnection(/*socket*/socket) {
var id = (1+Math.random()*4294967295).toString(16);
var id = RED.util.generateId();
if (node.isServer) {
node._clients[id] = socket;
node.emit('opened',{count:Object.keys(node._clients).length,id:id});

View File

@@ -69,7 +69,7 @@ module.exports = function(RED) {
var setupTcpClient = function() {
node.log(RED._("tcpin.status.connecting",{host:node.host,port:node.port}));
node.status({fill:"grey",shape:"dot",text:"common.status.connecting"});
var id = (1+Math.random()*4294967295).toString(16);
var id = RED.util.generateId();
client = net.connect(node.port, node.host, function() {
buffer = (node.datatype == 'buffer') ? Buffer.alloc(0) : "";
node.connected = true;
@@ -153,7 +153,7 @@ module.exports = function(RED) {
var server = net.createServer(function (socket) {
socket.setKeepAlive(true,120000);
if (socketTimeout !== null) { socket.setTimeout(socketTimeout); }
var id = (1+Math.random()*4294967295).toString(16);
var id = RED.util.generateId();
var fromi;
var fromp;
connectionPool[id] = socket;

View File

@@ -38,17 +38,18 @@ module.exports = function(RED) {
if (this.hdrout === true) { this.hdrout = "all"; }
var tmpwarn = true;
var node = this;
var re = new RegExp(',(?=(?:(?:[^"]*"){2})*[^"]*$)','g');
var re = new RegExp(node.sep.replace(/[-[\]{}()*+!<=:?.\/\\^$|#\s,]/g,'\\$&') + '(?=(?:(?:[^"]*"){2})*[^"]*$)','g');
// pass in an array of column names to be trimed, de-quoted and retrimed
var clean = function(col) {
// 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 template = clean(node.template,',');
var notemplate = template.length === 1 && template[0] === '';
node.hdrSent = false;
@@ -67,7 +68,7 @@ module.exports = function(RED) {
if (node.hdrout !== "none" && node.hdrSent === false) {
if ((template.length === 1) && (template[0] === '')) {
if (msg.hasOwnProperty("columns")) {
template = clean(msg.columns || "");
template = clean(msg.columns || "",",");
}
else {
template = Object.keys(msg.payload[0]);
@@ -80,7 +81,7 @@ module.exports = function(RED) {
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] && (msg.payload[s][t] !== 0)) { msg.payload[s][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;
@@ -93,7 +94,7 @@ module.exports = function(RED) {
}
else {
if ((template.length === 1) && (template[0] === '') && (msg.hasOwnProperty("columns"))) {
template = clean(msg.columns || "");
template = clean(msg.columns || "",",");
}
if ((template.length === 1) && (template[0] === '')) {
/* istanbul ignore else */
@@ -184,7 +185,7 @@ module.exports = function(RED) {
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);
template = clean(tmp,node.sep);
first = false;
}
else { tmp += line[i]; }

View File

@@ -32,7 +32,7 @@ module.exports = function(RED) {
var tag = node.tag;
if (msg.hasOwnProperty("select")) { tag = node.tag || msg.select; }
try {
var $ = cheerio.load(value);
var $ = cheerio.load(value,null,false);
var pay = [];
var count = 0;
$(tag).each(function() {
@@ -42,9 +42,11 @@ module.exports = function(RED) {
$(tag).each(function() {
if (node.as === "multi") {
var pay2 = null;
if (node.ret === "html") { pay2 = cheerio.load($(this).html().trim()).xml(); }
if (node.ret === "html") { pay2 = cheerio.load($(this).html().trim(),null,false).xml(); }
if (node.ret === "text") { pay2 = $(this).text(); }
if (node.ret === "attr") { pay2 = this.attribs; }
if (node.ret === "attr") {
pay2 = Object.assign({},this.attribs);
}
//if (node.ret === "val") { pay2 = $(this).val(); }
/* istanbul ignore else */
if (pay2) {
@@ -61,9 +63,12 @@ module.exports = function(RED) {
}
}
if (node.as === "single") {
if (node.ret === "html") { pay.push( cheerio.load($(this).html().trim()).xml() ); }
if (node.ret === "html") { pay.push( cheerio.load($(this).html().trim(),null,false).xml() ); }
if (node.ret === "text") { pay.push( $(this).text() ); }
if (node.ret === "attr") { pay.push( this.attribs ); }
if (node.ret === "attr") {
var attribs = Object.assign({},this.attribs);
pay.push( attribs );
}
//if (node.ret === "val") { pay.push( $(this).val() ); }
}
index++;

View File

@@ -17,8 +17,7 @@
module.exports = function(RED) {
"use strict";
const Ajv = require('ajv');
const ajv = new Ajv({allErrors: true, schemaId: 'auto'});
ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-04.json'));
const ajv = new Ajv({allErrors: true});
ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-06.json'));
function JSONNode(n) {

View File

@@ -219,6 +219,10 @@
value: "none",
label: label
}).text(label).appendTo(encSel);
$("<option/>", {
value: "setbymsg",
label: node._("file.encoding.setbymsg")
}).text(label).appendTo(encSel);
encodings.forEach(function(item) {
if(Array.isArray(item)) {
var group = $("<optgroup/>", {

View File

@@ -51,6 +51,10 @@ module.exports = function(RED) {
function processMsg(msg,nodeSend, done) {
var filename = node.filename || msg.filename || "";
var fullFilename = filename;
if (filename && RED.settings.fileWorkingDirectory && !path.isAbsolute(filename)) {
fullFilename = path.resolve(path.join(RED.settings.fileWorkingDirectory,filename));
}
if ((!node.filename) && (!node.tout)) {
node.tout = setTimeout(function() {
node.status({fill:"grey",shape:"dot",text:filename});
@@ -62,10 +66,11 @@ module.exports = function(RED) {
node.warn(RED._("file.errors.nofilename"));
done();
} else if (node.overwriteFile === "delete") {
fs.unlink(filename, function (err) {
fs.unlink(fullFilename, function (err) {
if (err) {
node.error(RED._("file.errors.deletefail",{error:err.toString()}),msg);
} else {
}
else {
if (RED.settings.verbose) {
node.log(RED._("file.status.deletedfile",{file:filename}));
}
@@ -74,11 +79,12 @@ module.exports = function(RED) {
done();
});
} else if (msg.hasOwnProperty("payload") && (typeof msg.payload !== "undefined")) {
var dir = path.dirname(filename);
var dir = path.dirname(fullFilename);
if (node.createDir) {
try {
fs.ensureDirSync(dir);
} catch(err) {
}
catch(err) {
node.error(RED._("file.errors.createfail",{error:err.toString()}),msg);
done();
return;
@@ -92,19 +98,24 @@ module.exports = function(RED) {
if (typeof data === "boolean") { data = data.toString(); }
if (typeof data === "number") { data = data.toString(); }
if ((node.appendNewline) && (!Buffer.isBuffer(data))) { data += os.EOL; }
var buf = encode(data, node.encoding);
var buf;
if (node.encoding === "setbymsg") {
buf = encode(data, msg.encoding || "none");
}
else { buf = encode(data, node.encoding); }
if (node.overwriteFile === "true") {
var wstream = fs.createWriteStream(filename, { encoding:'binary', flags:'w', autoClose:true });
var wstream = fs.createWriteStream(fullFilename, { encoding:'binary', flags:'w', autoClose:true });
node.wstream = wstream;
wstream.on("error", function(err) {
node.error(RED._("file.errors.writefail",{error:err.toString()}),msg);
done();
});
wstream.on("open", function() {
wstream.end(buf, function() {
wstream.once("close", function() {
nodeSend(msg);
done();
});
wstream.end(buf);
})
return;
}
@@ -116,7 +127,7 @@ module.exports = function(RED) {
// of the file. Check the file hasn't been deleted
// or deleted and recreated.
try {
var stat = fs.statSync(filename);
var stat = fs.statSync(fullFilename);
// File exists - check the inode matches
if (stat.ino !== node.wstreamIno) {
// The file has been recreated. Close the current
@@ -126,7 +137,8 @@ module.exports = function(RED) {
delete node.wstream;
delete node.wstreamIno;
}
} catch(err) {
}
catch(err) {
// File does not exist
recreateStream = true;
node.wstream.end();
@@ -135,10 +147,10 @@ module.exports = function(RED) {
}
}
if (recreateStream) {
node.wstream = fs.createWriteStream(filename, { encoding:'binary', flags:'a', autoClose:true });
node.wstream = fs.createWriteStream(fullFilename, { encoding:'binary', flags:'a', autoClose:true });
node.wstream.on("open", function(fd) {
try {
var stat = fs.statSync(filename);
var stat = fs.statSync(fullFilename);
node.wstreamIno = stat.ino;
} catch(err) {
}
@@ -154,14 +166,16 @@ module.exports = function(RED) {
nodeSend(msg);
done();
});
} else {
}
else {
// Dynamic filename - write and close the stream
node.wstream.end(buf, function() {
node.wstream.once("close", function() {
nodeSend(msg);
delete node.wstream;
delete node.wstreamIno;
done();
});
node.wstream.end(buf);
}
}
}
@@ -258,6 +272,10 @@ module.exports = function(RED) {
this.on("input",function(msg, nodeSend, nodeDone) {
var filename = (node.filename || msg.filename || "").replace(/\t|\r|\n/g,'');
var fullFilename = filename;
if (filename && RED.settings.fileWorkingDirectory && !path.isAbsolute(filename)) {
fullFilename = path.resolve(path.join(RED.settings.fileWorkingDirectory,filename));
}
if (!node.filename) {
node.status({fill:"grey",shape:"dot",text:filename});
}
@@ -276,10 +294,9 @@ module.exports = function(RED) {
ch = "\n";
type = "string";
}
var hwm;
var getout = false;
var rs = fs.createReadStream(filename)
var rs = fs.createReadStream(fullFilename)
.on('readable', function () {
var chunk;
var hwm = rs._readableState.highWaterMark;
@@ -340,16 +357,17 @@ module.exports = function(RED) {
nodeSend(msg);
}
else if (node.format === "lines") {
var m = { payload: spare,
topic:msg.topic,
parts: {
index: count,
count: count+1,
ch: ch,
type: type,
id: msg._msgid
}
};
var m = {
payload: spare,
topic:msg.topic,
parts: {
index: count,
count: count+1,
ch: ch,
type: type,
id: msg._msgid
}
};
nodeSend(m);
}
else if (getout) { // last chunk same size as high water mark - have to send empty extra packet.

View File

@@ -23,9 +23,13 @@ module.exports = function(RED) {
var getAllDirs = function (dir, filelist) {
filelist = filelist || [];
fs.readdirSync(dir).forEach(file => {
if (fs.statSync(path.join(dir, file)).isDirectory() ) {
filelist.push(path.join(dir, file));
getAllDirs(path.join(dir, file), filelist);
try {
if (fs.statSync(path.join(dir, file)).isDirectory() ) {
filelist.push(path.join(dir, file));
getAllDirs(path.join(dir, file), filelist);
}
} catch (error) {
//should we raise an error?
}
});
return filelist;

View File

@@ -1,18 +1,18 @@
[
{
"id": "2ebdd51e.c5d17a",
"id": "b05816ab.7f2b08",
"type": "comment",
"z": "4b63452d.672afc",
"name": "Convert array of JavaScript objects to CSV with column name header",
"info": "CSV node can convert an array of JavaScript objects to multi-line CSV text with column name header at first line.",
"x": 390,
"y": 1200,
"z": "c6ffdacd.d887e8",
"name": "Specify column names in input message",
"info": "Column names can be specified by `columns` property of incoming message.\n",
"x": 240,
"y": 200,
"wires": []
},
{
"id": "2b4d538d.ada07c",
"id": "39205b5c.690684",
"type": "inject",
"z": "4b63452d.672afc",
"z": "c6ffdacd.d887e8",
"name": "",
"props": [
{
@@ -30,41 +30,41 @@
"topic": "",
"payload": "",
"payloadType": "date",
"x": 260,
"y": 1260,
"x": 200,
"y": 260,
"wires": [
[
"3e5c9e8.5065b62"
"526b59ba.2fa068"
]
]
},
{
"id": "db02c7be.0984e8",
"id": "b78a407e.2d083",
"type": "csv",
"z": "4b63452d.672afc",
"z": "c6ffdacd.d887e8",
"name": "",
"sep": ",",
"hdrin": false,
"hdrout": "all",
"multi": "one",
"ret": "\\n",
"temp": "kind,price",
"temp": "",
"skip": "0",
"strings": true,
"include_empty_strings": "",
"include_null_values": "",
"x": 600,
"y": 1260,
"x": 750,
"y": 260,
"wires": [
[
"61f8b772.ddb1f8"
"8b7084dd.986f68"
]
]
},
{
"id": "3e5c9e8.5065b62",
"id": "526b59ba.2fa068",
"type": "template",
"z": "4b63452d.672afc",
"z": "c6ffdacd.d887e8",
"name": "JS object",
"field": "payload",
"fieldType": "msg",
@@ -72,18 +72,18 @@
"syntax": "plain",
"template": "[\n {\n \"kind\": \"Apple\",\n \"price\": 100,\n \"origin\": \"Canada\"\n },\n {\n \"kind\": \"Orange\",\n \"price\": 120,\n \"origin\": \"USA\"\n },\n {\n \"kind\": \"Banana\",\n \"price\": 80,\n \"origin\": \"Philippines\"\n }\n]",
"output": "json",
"x": 430,
"y": 1260,
"x": 370,
"y": 260,
"wires": [
[
"db02c7be.0984e8"
"b204077a.227778"
]
]
},
{
"id": "61f8b772.ddb1f8",
"id": "8b7084dd.986f68",
"type": "debug",
"z": "4b63452d.672afc",
"z": "c6ffdacd.d887e8",
"name": "",
"active": true,
"tosidebar": true,
@@ -92,8 +92,35 @@
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 780,
"y": 1260,
"x": 930,
"y": 260,
"wires": []
},
{
"id": "b204077a.227778",
"type": "change",
"z": "c6ffdacd.d887e8",
"name": "",
"rules": [
{
"t": "set",
"p": "columns",
"pt": "msg",
"to": "kind,price",
"tot": "str"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 570,
"y": 260,
"wires": [
[
"b78a407e.2d083"
]
]
}
]

View File

@@ -363,7 +363,7 @@
"keepalive": "Keep-Alive",
"cleansession": "Bereinigte Sitzung (clean session) verwenden",
"cleanstart": "Verwende bereinigten Start",
"use-tls": "Sichere Verbindung (SSL/TLS) verwenden",
"use-tls": "TLS",
"tls-config": "TLS-Konfiguration",
"verify-server-cert": "Server-Zertifikat überprüfen",
"compatmode": "MQTT 3.1 unterstützen",

View File

@@ -21,9 +21,10 @@
the body of the message.</p>
<p>The function is expected to return a message object (or multiple message objects), but can choose
to return nothing in order to halt a flow.</p>
<p>The <b>Setup</b> tab contains code that will be run whenever the node is started.
The <b>Close</b> tab contains code that will be run when the node is stopped.</p>
<p>If an promise object is returned from the setup code, input message processing starts after its completion.</p>
<p>The <b>On Start</b> tab contains code that will be run whenever the node is started.
The <b>On Stop</b> tab contains code that will be run when the node is stopped.</p>
<p>If the On Start code returns a Promise object, the node will not start handling messages
until the promise is resolved.</p>
<h3>Details</h3>
<p>See the <a target="_blank" href="http://nodered.org/docs/writing-functions.html">online documentation</a>
for more information on writing functions.</p>
@@ -52,10 +53,11 @@
pass <code>msg</code> as a second argument to <code>node.error</code>:</p>
<pre>node.error("Error",msg);</pre>
<h4>Accessing Node Information</h4>
<p>In the function block, id and name of the node can be referenced using the following properties:</p>
<p>The following properties are available to access information about the node:</p>
<ul>
<li><code>node.id</code> - id of the node</li>
<li><code>node.name</code> - name of the node</li>
<li><code>node.outputCount</code> - number of node outputs</li>
</ul>
<h4>Using environment variables</h4>
<p>Environment variables can be accessed using <code>env.get("MY_ENV_VAR")</code>.</p>

View File

@@ -22,6 +22,12 @@
<dd>Sets the delay, in milliseconds, to be applied to the message. This
option only applies if the node is configured to allow the message to
override the configured default delay interval.</dd>
<dt class="optional">rate <span class="property-type">number</span></dt>
<dd>Sets the rate value in milliseconds between messages.
This node overwrites the existing rate value defined in the node configuration
when it receives the message which contains <code>msg.rate</code> value in milliSeconds.
This option only applies if the node is configured to allow the message to
override the configured default rate interval.</dd>
<dt class="optional">reset</dt>
<dd>If the received message has this property set to any value, all
outstanding messages held by the node are cleared without being sent.</dd>
@@ -32,15 +38,17 @@
<h3>Details</h3>
<p>When configured to delay messages, the delay interval can be a fixed value,
a random value within a range or dynamically set for each message.
Each message is delayed independently of any other message, based on
Each message is delayed independently of any other message, based on
the time of its arrival.
</p>
<p>When configured to rate limit messages, their delivery is spread across
the configured time period. The status shows the number of messages currently in the queue.
It can optionally discard intermediate messages as they arrive.</p>
</p>
<p> If set to allow override of the rate, the new rate will be applied immediately,
and will remain in effect until changed again, the node is reset, or the flow is restarted.</p>
<p>The rate limiting can be applied to all messages, or group them according to
their <code>msg.topic</code> value. When grouping, intermerdiate messages are
their <code>msg.topic</code> value. When grouping, intermediate messages are
automatically dropped. At each time interval, the node can either release
the most recent message for all topics, or release the most recent message
for the next topic.

View File

@@ -32,6 +32,7 @@
},
"inject": {
"inject": "inject",
"injectNow": "inject now",
"repeat": "repeat = __repeat__",
"crontab": "crontab = __crontab__",
"stopped": "stopped",
@@ -170,14 +171,16 @@
"passphrase": "Passphrase",
"ca": "CA Certificate",
"verify-server-cert":"Verify server certificate",
"servername": "Server Name"
"servername": "Server Name",
"alpnprotocol": "ALPN Protocol"
},
"placeholder": {
"cert":"path to certificate (PEM format)",
"key":"path to private key (PEM format)",
"ca":"path to CA certificate (PEM format)",
"passphrase":"private key passphrase (optional)",
"servername":"for use with SNI"
"servername":"for use with SNI",
"alpnprotocol":"for use with ALPN"
},
"error": {
"missing-file": "No certificate/key file provided"
@@ -227,6 +230,7 @@
"error": {
"externalModuleNotAllowed": "Function node not allowed to load external modules",
"moduleNotAllowed": "Module __module__ not allowed",
"externalModuleLoadError": "Function node failed to load external modules",
"moduleLoadError": "Failed to load module __module__: __error__",
"moduleNameError": "Invalid module variable name: __name__",
"moduleNameReserved": "Reserved variable name: __name__",
@@ -276,6 +280,7 @@
"rate": "Rate",
"msgper": "msg(s) per",
"dropmsg": "drop intermediate messages",
"allowrate": "allow msg.rate (in ms) to override rate",
"label": {
"delay": "delay",
"variable": "variable",
@@ -301,9 +306,8 @@
}
}
},
"error": {
"buffer": "buffer exceeded 1000 messages",
"buffer1": "buffer exceeded 10000 messages"
"errors": {
"too-many" : "too many pending messages in delay node"
}
},
"trigger": {
@@ -877,6 +881,7 @@
},
"encoding": {
"none": "default",
"setbymsg": "set by msg.encoding",
"native": "Native",
"unicode": "Unicode",
"japanese": "Japanese",

View File

@@ -91,7 +91,7 @@
</ul>
</dd>
<dt class="optional">complete</dt>
<dd>If set, the node will append the payload, and then send the output message in its current state.
<dd>If set, the node will append the payload, and then send the output message in its current state.
If you don't wish to append the payload, delete it from the msg.</dd>
</dl>
<h3>Details</h3>
@@ -150,6 +150,7 @@
<p>By default, the reduce expression is applied in order, from the first
to the last message of the sequence. It can optionally be applied in
reverse order.</p>
<p>$N is the number of messages that arrive - even if they are identical.</p>
</dl>
<p><b>Example:</b> the following settings, given a sequence of numeric values,
calculates the average value:

View File

@@ -21,6 +21,8 @@
<dl class="message-properties">
<dt class="optional">filename <span class="property-type">string</span></dt>
<dd>If not configured in the node, this optional property sets the name of the file to be updated.</dd>
<dt class="optional">encoding <span class="property-type">string</span></dt>
<dd>If encoding is configured to be set by msg, then this optional property can set the encoding.</dt>
</dl>
<h3>Output</h3>
<p>On completion of write, input message is sent to output port.</p>

View File

@@ -19,8 +19,8 @@
<p>入力メッセージは<code>msg</code>JavaScript</p>
<p><code>msg</code><code>msg.payload</code></p>
<p>通常コードはメッセージオブジェクト(もしくは複数のメッセージオブジェクト)を返却します後続フローの実行を停止したい場合はオブジェクトを返却しなくてもかまいません</p>
<p>Node-REDの開始時もしくはフローの設定をデプロイした際実行される初期化コードを<b>初期化処理</b><b></b></p>
<p>初期化処理タブの返却値としてPromiseを返却すると入力メッセージの処理を開始する前にその完了を待ちます</p>
<p><b>初期化処理</b><b></b></p>
<p>初期化処理タブの返却値としてPromiseオブジェクトを返却すると入力メッセージの処理を開始する前にその完了を待ちます</p>
<h3>詳細</h3>
<p>コードの書き方の詳細については<a target="_blank" href="http://nodered.org/docs/writing-functions.html">オンラインドキュメント</a></p>
<h4>メッセージの送信</h4>
@@ -44,10 +44,11 @@
<p>catchードを用いてエラー処理が可能ですcatchードで処理させるためには<code>msg</code><code>node.error</code>:</p>
<pre>node.error("エラー",msg);</pre>
<h4>ノード情報の参照</h4>
<p>コード中ではードのIDおよび名前を以下のプロパティで参照できます:</p>
<p>ノードに関する情報を参照するための以下のプロパティを利用できます:</p>
<ul>
<li><code>node.id</code> - ID</li>
<li><code>node.name</code> - </li>
<li><code>node.outputCount</code> - </li>
</ul>
<h4>環境変数の利用</h4>
<p>環境変数は<code>env.get("MY_ENV_VAR")</code></p>

View File

@@ -20,6 +20,8 @@
<dl class="message-properties">
<dt class="optional">delay <span class="property-type">数値</span></dt>
<dd>メッセージの遅延時間をミリ秒単位で設定しますこれはノードの設定でデフォルトの遅延時間を上書きできるようノードを設定した場合にのみ適用します</dd>
<dt class="optional">rate <span class="property-type">数値</span></dt>
<dd>メッセージ間の流量値をミリ秒単位で設定します本ノードは<code>msg.rate</code></dd>
<dt class="optional">reset</dt>
<dd>受信メッセージでこのプロパティを任意の値に設定するとノードが保持する全ての未送信メッセージをクリアします</dd>
<dt class="optional">flush</dt>
@@ -28,5 +30,6 @@
<h3>詳細</h3>
<p>メッセージを遅延させるように設定する場合遅延時間は固定値範囲内の乱数値メッセージ毎の動的な指定値のいずれかを指定できます</p>
<p>流量制御する場合メッセージは指定した時間間隔内に分散して送信しますキューに残っているメッセージ数はノードのステータスに表示されます受け取った中間メッセージを破棄することも可能です</p>
<p>流量値を上書きできるように設定されている場合新しい流量値はすぐに適用されますこの流量値は再度変更されるまで本ノードがリセットされるまでまたはフローが再実行されるまで有効です</p>
<p>流量制御は全てのメッセージに適用することも<code>msg.topic</code></p>
</script>

View File

@@ -227,6 +227,7 @@
"error": {
"externalModuleNotAllowed": "Functionードは、外部モジュールを読み込みできません",
"moduleNotAllowed": "モジュール __module__ は利用できません",
"externalModuleLoadError": "Functionードは、外部モジュールの読み込みに失敗しました",
"moduleLoadError": "モジュール __module__ の読み込みに失敗しました: __error__",
"moduleNameError": "モジュール変数名が不正です: __name__",
"moduleNameReserved": "予約された変数名です: __name__",
@@ -276,6 +277,7 @@
"rate": "流量",
"msgper": "メッセージ/",
"dropmsg": "中間メッセージを削除",
"allowrate": "msg.rate(ミリ秒単位)で流量値を上書き",
"label": {
"delay": "delay",
"variable": "variable",
@@ -301,9 +303,8 @@
}
}
},
"error": {
"buffer": "バッファ上限の1000メッセージを超えました",
"buffer1": "バッファ上限の10000メッセージを超えました"
"errors": {
"too-many": "delayード内で保持しているメッセージが多すぎます"
}
},
"trigger": {
@@ -875,6 +876,7 @@
},
"encoding": {
"none": "デフォルト",
"setbymsg": "msg.encodingで設定",
"native": "ネイティブ",
"unicode": "UNICODE",
"japanese": "日本",

View File

@@ -20,6 +20,8 @@
<dl class="message-properties">
<dt class="optional">filename <span class="property-type">文字列</span></dt>
<dd>対象ファイル名をノードに設定していない場合このプロパティでファイルを指定できます</dd>
<dt class="optional">encoding <span class="property-type">文字列</span></dt>
<dd>エンコーディングをmsgで設定する構成にした際はこの任意のプロパティでエンコーディングを設定できます</dt>
</dl>
<h3>出力</h3>
<p>書き込みの完了時入力メッセージを出力端子に送出します</p>

View File

@@ -272,10 +272,6 @@
"singular": "일"
}
}
},
"error": {
"buffer": "버퍼 상한의 1000메세지를 넘었습니다",
"buffer1": "버퍼 상한의 10000메세지를 넘었습니다"
}
},
"trigger": {
@@ -333,7 +329,7 @@
"port": "포트",
"keepalive": "킵 얼라이브 시간",
"cleansession": "세션 초기화",
"use-tls": "SSL/TLS접속을 사용",
"use-tls": "사용TLS",
"tls-config": "TLS설정",
"verify-server-cert": "서버인증서를 확인",
"compatmode": "구 MQTT 3.1서포트"

View File

@@ -352,7 +352,7 @@
"port": "Порт",
"keepalive": "Keep-alive время (сек)",
"cleansession": "Использовать чистую сессию",
"use-tls": "Включить безопасное (SSL/TLS) соединение",
"use-tls": "TLS",
"tls-config":"Конфигурация TLS",
"verify-server-cert":"Проверить сертификат сервера",
"compatmode": "Использовать устаревшую поддержку MQTT 3.1"

View File

@@ -290,10 +290,6 @@
"singular": "天"
}
}
},
"error": {
"buffer": "缓冲了超过 1000 条信息",
"buffer1": "缓冲了超过 10000 条信息"
}
},
"trigger": {
@@ -353,7 +349,7 @@
"port": "端口",
"keepalive": "Keepalive计时(秒)",
"cleansession": "使用新的会话",
"use-tls": "使用安全连接 (SSL/TLS)",
"use-tls": "使用 TLS",
"tls-config": "TLS 设置",
"verify-server-cert": "验证服务器证书",
"compatmode": "使用旧式MQTT 3.1支持"

View File

@@ -353,7 +353,7 @@
"port": "埠",
"keepalive": "Keepalive計時(秒)",
"cleansession": "使用新的會話",
"use-tls": "使用安全連接 (SSL/TLS)",
"use-tls": "使用 TLS",
"tls-config": "TLS 設置",
"verify-server-cert": "驗證伺服器憑證",
"compatmode": "使用舊式MQTT 3.1支援"

View File

@@ -15,17 +15,19 @@
}
],
"dependencies": {
"ajv": "6.12.6",
"acorn": "8.3.0",
"acorn-walk": "8.1.0",
"ajv": "8.6.0",
"body-parser": "1.19.0",
"cheerio": "0.22.0",
"cheerio": "1.0.0-rc.10",
"content-type": "1.0.4",
"cookie-parser": "1.4.5",
"cookie": "0.4.1",
"cors": "2.8.5",
"cron": "1.7.2",
"cronosjs": "1.7.1",
"denque": "1.5.0",
"form-data": "4.0.0",
"fs-extra": "9.1.0",
"fs-extra": "10.0.0",
"fs.notify": "0.0.4",
"got": "11.8.2",
"hash-sum": "2.0.0",
@@ -41,9 +43,9 @@
"raw-body": "2.4.1",
"request": "2.88.0",
"tough-cookie": "4.0.0",
"uuid":"8.3.2",
"ws": "6.2.1",
"uuid": "8.3.2",
"ws": "7.4.6",
"xml2js": "0.4.23",
"iconv-lite": "0.6.2"
"iconv-lite": "0.6.3"
}
}