功能简述
- 此节点有一个输入,两个输出;
- 此节点可完成信息的限速分流功能,当信息高速输入时,节点进行限速处理(例:两秒一次),到指定时间后信息由一口输出,若未到指定时间(两秒)信息由二口输出。
节点设计
界面设计
节点界面中可设置信息传输速度,每多少秒限制多少条信息。
代码如下:
<script type="text/html" data-template-name="delaytest">
<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" date-i18n="[placeholder]node-red:common.label.name" autocomplete="off"
dir placeholder="name">
</div>
<div id="rate-details">
<div class="form-row">
<label for="node-input-rate"><i class="fa fa-clock-o"></i> <span data-i18n="delay.rate">速度</span></label>
<label for="node-input-rateUnits"><span data-i18n="delay.msgper">每</span></label>
<input type="text" id="node-input-nbRateUnits" placeholder="1"
style="text-align:end; width:40px !important">
<select id="node-input-rateUnits" style="width:90px !important">
<option value="second" data-i18n="delay.label.units.second.singular">秒</option>
<option value="minute" data-i18n="delay.label.units.minute.singular">分钟</option>
<option value="hour" data-i18n="delay.label.units.hour.singular">小时</option>
<option value="day" data-i18n="delay.label.units.day.singular">天</option>
</select>一条信息
</div>
</div>
</script>
<script type="text/javascript">
RED.nodes.registerType('delaytest', {
category: 'function',
color: "#E6E0F8",
defaults: {
name: {
value: "name"
},
rate: {
value: "1",
required: true,
validate: function (v) {
return RED.validators.number(v) && (v >= 0);
}
},
nbRateUnits: {
value: "1",
required: false,
validate: RED.validators.regex(/\d+|/)
},
rateUnits: {
value: "second"
}
},
inputs: 1,
outputs: 2,
icon: "timer.svg",
label: function () {
if (this.name) {
return this.name;
}
var rate = this.rate + " msg/" + (this.rateUnits ? (this.nbRateUnits > 1 ? this.nbRateUnits :
'') + this.rateUnits.charAt(0) : "s");
return this._("delay.label.limit") + " " + rate;
},
labelStyle: function () {
return this.name ? "node_label_italic" : "";
},
oneditprepare: function () {
var node = this;
$("#node-input-rate").spinner({
min: 1
});
$("#node-input-nbRateUnits").spinner({
min: 1
});
$("#node-input-nbRateUnits").on('change keyup', function () {
var $this = $(this);
var val = parseInt($this.val());
var type = "singular";
if (val > 1) {
type = "plural";
}
if ($this.attr("data-type") == type) {
return;
}
$this.attr("data-type", type);
$("#node-input-rateUnits option").each(function () {
var $option = $(this);
var key = "delay.label.units." + $option.val() + "." + type;
$option.attr('data-i18n', 'node-red:' + key);
$option.html(node._(key));
});
});
}
});
</script>
功能设计
这里主要参考官方delay节点中的限速部分代码。
代码如下:
module.exports = function (RED) {
"use strict";
function DelatTestNode(n) {
RED.nodes.createNode(this, n);
this.rateUnits = n.rateUnits;
// 速率计时时间单位选项
if (n.rateUnits === "minute") {
this.rate = (60 * 1000) / n.rate;
} else if (n.rateUnits === "hour") {
this.rate = (60 * 60 * 1000) / n.rate;
} else if (n.rateUnits === "day") {
this.rate = (24 * 60 * 60 * 1000) / n.rate;
} else { // Default to seconds
this.rate = 1000 / n.rate;
}
// 规定限制速率:每条信息‘nbRateUnite’秒
this.rate *= (n.nbRateUnits > 0 ? n.nbRateUnits : 1);
this.name = n.name;
this.buffer = [];
this.intervalID = -1;
var node = this;
// reportDepth
node.reportDepth = function () {
if (!node.busy) {
// 赋值node.busy以下函数的运行时间
node.busy = setTimeout(function () {
// 判断node.buffer.length长度是否大于0,即输入的是否为正常字符串
if (node.buffer.length > 0) {
//显示nodered节点状态
node.status({
text: node.buffer.length
});
} else {
node.status({});
}
node.busy = null;
}, 500);
}
}
// 限制速率、每一个topic函数
node.intervalID = setInterval(function () {
while (node.buffer.length > 0) { // send the whole queue
var msg1 = null;
var msg2 = {
payload: node.buffer.shift()
};
node.send([msg1, msg2])
}
node.reportDepth();
}, node.rate);
node.on("input", function (msg) {
if (msg.hasOwnProperty("reset")) {
if (node.intervalID !== -1) {
clearInterval(node.intervalID);
node.intervalID = -1;
}
delete node.lastSent;
node.buffer = [];
node.status({
text: "reset"
});
return;
}
var timeSinceLast;
timeSinceLast = process.hrtime(node.lastSent);
if (((timeSinceLast[0] * 1000000000) + timeSinceLast[1]) > (node.rate * 1000000)) {
node.lastSent = process.hrtime();
var msg1 = null;
var msg2 = {
payload: msg.payload
};
node.send([msg2, msg1]);
} else {
var msg1 = null;
var msg2 = {
payload: msg.payload
};
node.send([msg1, msg2]);
}
});
node.on("close", function () {
clearInterval(node.intervalID);
node.buffer = [];
node.status({});
});
}
RED.nodes.registerType("delaytest", DelatTestNode);
}
功能实现
串行总线(RS485)由于其非平衡传输特性的限制,广泛应用主从MODBUS RTU(ASCII)协议。主从协议严格遵循请求应答机制,尤其在主机向总线中各从机查询数据时,需要逐个设备节点、逐片寄存器发起请求。实际应用中称之为MODBUS总线数据轮询。
基于此节点的分流限速功能,可用于不同采集周期情况下的多设备数据轮询。如下图所示:
假设在某应用场景下,三个设备分别需要每一秒,两秒,三秒采集一次数据,各设备按照指定周期时间采集数据,为避免占用数据传输总线冲突,当设备采集时间相同时,切换为轮询的方式依次采集数据。
即在第一秒时,信息传递路径为:
第二秒时,信息传递路径为:
第三秒时,信息传递路径为:
之后的时间以此类推,在此基础上也可以添加更多设备或设置其他采集周期的设备。