创建节点

Node-RED 扩展的主要方式是将新节点添加到其调色板中。

节点可以作为 npm 模块发布到公共 npm 存储库,并添加到 Node-RED 流库中以使其可供社区使用。

从 Node-RED 1.3 开始

一般指导

创建新节点时,需要遵循一些一般原则。这些反映了核心节点和 HELP 采用的方法提供了一致的用户体验。

节点应:

  • 明确他们的目标。

    公开 API 的所有可能选项的节点可能不太有用一组节点,每个节点都有一个用途。

  • 易于使用,无论底层功能如何。

    隐藏复杂性并避免使用行话或特定于领域的知识。

  • 宽容它接受的消息属性类型。

    消息属性可以是字符串、数字、布尔值、缓冲区、对象、数组 或 null。节点在面对其中任何一个时都应该做正确的事情。

  • 他们发送的内容要一致。

    节点应该记录它们向消息添加的属性,并且它们应该 他们的行为要一致和可预测。

  • 坐在流程的开头、中间或结尾 - 不能同时全部完成。

  • catch 错误。

    如果节点抛出未捕获的错误,Node-RED 将停止整个流,因为 系统的状态不再已知。

    在可能的情况下,节点必须捕获错误或为任何他们进行的异步调用。

创建第一个节点

节点是在部署流时创建的,它们可能会在流运行时发送和接收一些消息,并在部署下一个流时删除。

它们由一对文件组成:

  • 定义节点功能的 JavaScript 文件,
  • 一个 HTML 文件,用于定义节点的属性、编辑对话框和帮助文本。

一个文件用于将其全部打包为 npm 模块package.json

创建简单节点

此示例将演示如何创建将消息负载转换为所有小写字符的节点。

确保您的系统上安装了 Node.js 的当前 LTS 版本。在撰写本文的时间是 10.x。

创建一个目录,您将在其中开发代码。在该目录中,创建以下文件:

  • package.json
  • lower-case.js
  • lower-case.html

package.json

这是 Node.js 模块用来描述其内容的标准文件。

要生成标准文件,可以使用这个命令。这将询问一系列问题,以帮助为文件,尽可能使用合理的默认值。出现提示时,为其指定名称。package.json npm init node-red-contrib-example-lower-case

生成后,您必须添加一个部分:node-red

1
2
3
4
5
6
7
8
9
{
"name" : "node-red-contrib-example-lower-case",
...
"node-red" : {
"nodes": {
"lower-case": "lower-case.js"
}
}
}

这会告诉运行时模块包含哪些节点文件。

有关如何打包节点的更多信息,包括在发布节点之前应设置的命名和其他属性的要求,请参阅打包指南

注意请不要将此示例节点发布到 npm!

lower-case.js

1
2
3
4
5
6
7
8
9
10
11
module.exports = function(RED) {
function LowerCaseNode(config) {
RED.nodes.createNode(this,config);
var node = this;
node.on('input', function(msg) {
msg.payload = msg.payload.toLowerCase();
node.send(msg);
});
}
RED.nodes.registerType("lower-case",LowerCaseNode);
}

该节点包装为 Node.js 模块。该模块导出一个函数,该函数在 runtime 启动时加载节点时被调用。该函数使用单个参数调用,该参数为模块提供对 Node-RED 运行时 API 的访问。RED

节点本身由一个函数定义,每当创建节点的新实例时,都会调用该函数。它传递一个对象,其中包含在 Flow Editor 中设置的特定于节点的属性。 LowerCaseNode

该函数调用该函数初始化所有节点共享的功能。在此之后,特定于节点的代码将存在。RED.nodes.createNode

在这种情况下,节点将一个侦听器注册到事件中,每当消息到达节点时,该事件就会被调用。在此侦听器中,它将有效负载更改为小写,然后调用函数以在流中传递消息。input send

最后,使用节点的名称。LowerCaseNode lower-case

如果节点具有任何外部模块依赖项,则必须将它们包含在其文件的部分中。dependencies package.json

有关节点的运行时部分的更多信息,请参阅此处

lower-case.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<script type="text/javascript">
RED.nodes.registerType('lower-case',{
category: 'function',
color: '#a6bbcf',
defaults: {
name: {value:""}
},
inputs: 1,
outputs: 1,
icon: "file.svg",
label: function() {
return this.name||"lower-case";
}
});
</script>

<script type="text/html" data-template-name="lower-case">
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>

<script type="text/html" data-help-name="lower-case">
<p>A simple node that converts the message payloads into all lower-case characters</p>
</script>

节点的 HTML 文件提供以下内容:

  • 向编辑器注册的主节点定义
  • 编辑模板
  • 帮助文本

在此示例中,节点具有单个可编辑属性。虽然不是必须的,则此属性有一个广泛使用的约定来帮助区分在单个流中节点的多个实例之间 name

有关节点的编辑器部分的更多信息,请参阅此处

在 Node-RED 中测试您的节点

如上所述创建基本 node 模块后,您可以将其安装到 Node-RED 运行时中。

要在本地测试 node 模块,可以使用 npm install <folder> 命令。这允许在本地目录中开发节点,并且 在开发过程中将其链接到本地 Node-RED 安装。

在 node-red 用户目录中,通常运行:~/.node-red

1
npm install <location of node module>

例如,在 Mac OS 或 Linux 上,如果您的节点位于您身边,您将执行以下作:~/dev/node-red-contrib-example-lower-case

1
2
cd ~/.node-red
npm install ~/dev/node-red-contrib-example-lower-case

在 Windows 上,您可以执行以下作:

1
2
cd C:\Users\my_name\.node_red
npm install C:\Users\my_name\Documents\GitHub\node-red-contrib-example-lower-case

这将创建一个指向 node module 项目目录的符号链接,以便 Node-RED 在启动时发现该节点。只需重新启动 Node-RED 即可获取对节点文件的任何更改。在 Windows 上,同样使用 npm 5.x 或更高版本:~/.node-red/node_modules

注意 :将自动在位于用户目录中的文件中为您的模块添加一个条目。如果您不希望它执行此作,请使用命令的选项。npm package.json --no-save npm install

单元测试

为了支持单元测试,可以使用名为 node-red-node-test-helper 的 npm 模块。test-helper 是一个基于 Node-RED 运行时构建的框架,可以更轻松地测试节点。

使用此框架,您可以创建测试流,然后断言您的节点属性和输出按预期工作。例如,要向小写节点添加单元测试,您可以在 node 模块包中添加一个文件夹,其中包含一个名为 test _spec.js

测试/lower-case_spec.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
var helper = require("node-red-node-test-helper");
var lowerNode = require("../lower-case.js");

describe('lower-case Node', function () {

afterEach(function () {
helper.unload();
});

it('should be loaded', function (done) {
var flow = [{ id: "n1", type: "lower-case", name: "test name" }];
helper.load(lowerNode, flow, function () {
var n1 = helper.getNode("n1");
n1.should.have.property('name', 'test name');
done();
});
});

it('should make payload lower case', function (done) {
var flow = [{ id: "n1", type: "lower-case", name: "test name",wires:[["n2"]] },
{ id: "n2", type: "helper" }];
helper.load(lowerNode, flow, function () {
var n2 = helper.getNode("n2");
var n1 = helper.getNode("n1");
n2.on("input", function (msg) {
msg.should.have.property('payload', 'uppercase');
done();
});
n1.receive({ payload: "UpperCase" });
});
});
});

这些测试会检查节点是否已正确加载到运行时中,以及它是否按预期正确地将负载更改为小写。

这两个测试都使用提供待测节点和测试流将节点加载到运行时中:第一个测试检查运行时中的节点是否具有正确的 name 属性。第二个测试使用帮助程序节点来检查该节点的输出是否实际上是小写的。helper.load

helper 模块包含从 Node-RED 核心节点获取的其他测试示例。有关 helper 模块的更多信息,请参阅关联的 README。

JavaScript 文件

节点文件定义节点的运行时行为。.js

Node 构造函数

节点由构造函数定义,该函数可用于创建节点的新实例。该函数将注册到运行时,以便在流中部署相应类型的节点时可以调用该函数。

该函数将传递一个对象,其中包含在流编辑器中设置的属性。

它必须做的第一件事是调用函数来初始化所有节点共享的功能。在此之后,特定于节点的代码将存在。RED.nodes.createNode

1
2
3
4
5
6
7
function SampleNode(config) {
RED.nodes.createNode(this,config);
// node-specific code goes here

}

RED.nodes.registerType("sample",SampleNode);

接收消息

节点在事件上注册侦听器,以接收来自流中上游节点的消息 input

在 Node-RED 1.0 中,引入了一种新的侦听器样式,允许节点在处理完消息后通知运行时。这向 listener 函数添加了两个新参数。需要注意确保节点仍然可以安装在 Node-RED 0.x 中,因为 Node-RED 0.x 不使用这种新样式的侦听器。

1
2
3
4
5
6
7
8
9
10
this.on('input', function(msg, send, done) {
// do something with 'msg'

// Once finished, call 'done'.
// This call is wrapped in a check that 'done' exists
// so the node will work in earlier versions of Node-RED (<1.0)
if (done) {
done();
}
});

处理错误

如果节点在处理消息时遇到错误,它应该将错误的详细信息传递给函数 done

这将触发同一选项卡上存在的任何 Catch 节点,从而允许用户构建流来处理错误。

同样,如果节点安装在不提供该功能的 Node-RED 0.x 中,则需要小心。在这种情况下,它应该使用 :done node.error

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let node = this;
this.on('input', function(msg, send, done) {
// do something with 'msg'

// If an error is hit, report it to the runtime
if (err) {
if (done) {
// Node-RED 1.0 compatible
done(err);
} else {
// Node-RED 0.x compatible
node.error(err, msg);
}
}
});

发送消息

如果节点位于流的开头并生成消息以响应外部事件,则应使用节点对象的 send 上的函数

1
2
var msg = { payload:"hi" }
this.send(msg);

如果节点想要从事件侦听器内部发送,作为对接收消息的响应,它应该使用传递给侦听器的函数 function:input send

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let node = this;
this.on('input', function(msg, send, done) {
// For maximum backwards compatibility, check that send exists.
// If this node is installed in Node-RED 0.x, it will need to
// fallback to using `node.send`
send = send || function() { node.send.apply(node,arguments) }

msg.payload = "hi";
send(msg);

if (done) {
done();
}
});

如果为 null,则不发送消息。msg

如果节点发送消息以响应收到消息,则它应该重用收到的消息,而不是创建新的消息对象。这可确保为流的其余部分保留消息上的现有属性。

多个输出

如果节点具有多个输出,则可以将消息数组传递给 send,每个消息都发送到相应的输出。

1
this.send([ msg1 , msg2 ]);

多条消息

可以通过在此数组中传递消息数组来将多条消息发送到特定输出:

1
this.send([ [msgA1 , msgA2 , msgA3] , msg2 ]);

关闭节点

每当部署新流时,都会删除现有节点。如果它们中的任何一个在发生这种情况时需要整理状态,例如断开与远程系统的连接,则应在 close 事件上注册一个侦听器。

1
2
3
this.on('close', function() {
// tidy up any state
});

如果节点需要做任何异步工作来完成整理,注册的侦听器应该接受一个参数,该参数是在所有工作完成时要调用的函数。

1
2
3
4
5
this.on('close', function(done) {
doSomethingWithACallback(function() {
done();
});
});

自 Node-RED 0.17 起

如果注册的侦听器接受两个参数,则第一个参数将是一个布尔标志,指示节点是由于已完全删除而被关闭,还是只是重新启动。如果节点已被禁用,它也将设置为 true

1
2
3
4
5
6
7
8
this.on('close', function(removed, done) {
if (removed) {
// This node has been disabled/deleted
} else {
// This node is being restarted
}
done();
});

超时行为

自 Node-RED 0.17 起

在 Node-RED 0.17 之前,运行时将无限期地等待函数被调用。如果节点无法调用运行时,这将导致运行 done 时挂起。

在 0.17 及更高版本中,如果节点超时时间超过 15 秒,则运行时将超时节点。将记录错误,并且运行时将继续运行。

记录事件

如果节点需要将某些内容记录到控制台,它可以使用以下函数之一:

1
2
3
4
5
6
7
this.log("Something happened");
this.warn("Something happened you should know about");
this.error("Oh no, something bad happened");

// Since Node-RED 0.17
this.trace("Log some internal detail not needed for normal operation");
this.debug("Log something more details for debugging the node's behaviour");

和消息也会发送到流编辑器的 debug 的 warn error 选项卡。

设置状态

在运行时,节点能够与编辑器 UI 共享状态信息。这是通过调用以下函数来完成的:status

1
this.status({fill:"red",shape:"ring",text:"disconnected"});

状态 api 的详细信息可以在这里找到。

自定义节点设置

节点可能希望在用户文件中公开配置选项。settings.js

任何设置的名称都必须符合以下要求:

  • 该名称必须以相应的节点类型为前缀。
  • 该设置必须使用驼峰式大小写 - 有关更多信息,请参阅下文。
  • 节点不得要求用户进行设置 - 它应该有一个合理的默认值。

例如,如果节点类型想要公开名的设置,则设置名称应为 sample-node colour sampleNodeColour

在运行时中,节点可以将设置引用为 RED.settings.sampleNodeColour

向编辑器公开设置

自 Node-RED 0.17 起

在某些情况下,节点可能希望向编辑器公开设置的值。如果是这样,则节点必须调用:registerType

1
2
3
4
5
6
7
8
RED.nodes.registerType("sample",SampleNode, {
settings: {
sampleNodeColour: {
value: "red",
exportable: true
}
}
});
  • valuefield 指定设置应采用的默认值。
  • exportable指示运行时使设置可供编辑器使用。

与运行时一样,节点可以像在编辑器中一样引用该设置。RED.settings.sampleNodeColour

如果节点尝试注册不符合命名要求的设置,将记录错误。

HTML 文件

节点文件定义节点在编辑器中的显示方式。它包含三个不同的部分,每个部分都包装在自己的标签中:.html <script>

  1. 向编辑器注册的主节点定义。这定义了诸如调色板类别、可编辑属性()和使用什么图标。它位于常规 javascript 脚本标记 defaults

  2. 定义节点的 Edit 对话框内容的 Edit 模板。它是在 type 为 set 的节点类型的 text/html data-template-name 脚本中定义的。

  3. 显示在 Info sidebar (信息) 侧边栏选项卡中的帮助文本。它是在 type 为 set 的节点类型的 text/html data-help-name 脚本中定义的。

定义节点

必须使用该函数 RED.nodes.registerType 向编辑器注册节点。

此函数采用两个参数;节点的类型及其定义:

1
2
3
4
5
<script type="text/javascript">
RED.nodes.registerType('node-type',{
// node definition
});
</script>

节点类型

节点类型在整个编辑器中用于标识节点。它必须与相应 RED.nodes.registerType .js 文件中调用 to 使用的值匹配。

节点定义

节点定义包含有关编辑器所需节点的所有信息。它是一个具有以下属性的对象:

  • category: (string) 节点显示在调色板类别中
  • defaults:(对象)节点的可编辑属性
  • credentials:(object) 节点的凭证属性
  • inputs: (number) 节点有多少个输入,0 或 1
  • outputs: (number) 节点具有多少个输出。可以是 0 或更多。
  • color: (string) 要使用的背景颜色
  • paletteLabel: (string|function) 要在调色板中使用的标签
  • label: (string|function) 要在工作区中使用的标签
  • labelStyle: (string|function) 要应用于标签的样式
  • inputLabels: (string|function) 将鼠标悬停在节点的输入端口时添加的可选标签
  • outputLabels: (string|function) 将鼠标悬停在节点的输出端口时要添加的可选标签
  • icon: (string) 要使用的图标
  • align: (string) 图标和标签的对齐方式
  • button: (object) 将按钮添加到节点的边缘。
  • oneditprepare: (函数)在构建编辑对话框时调用。请参阅自定义编辑行为
  • oneditsave: (函数) 在编辑对话框正常时调用。请参阅自定义编辑行为
  • oneditcancel:(函数)在取消编辑对话框时调用。请参阅自定义编辑行为
  • oneditdelete: (函数)在按下配置节点的编辑对话框中的 Delete 按钮时调用。请参阅自定义编辑行为
  • oneditresize: (函数) 在调整编辑对话框的大小时调用。请参阅自定义编辑行为
  • onpaletteadd:(函数)在将节点类型添加到调色板时调用。
  • onpaletteremove:(函数)在从调色板中删除节点类型时调用。

“编辑”对话框

节点的编辑模板描述其编辑对话框的内容。

1
2
3
4
5
6
7
<script type="text/html" data-template-name="node-type">
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-tips"><b>Tip:</b> This is here to help.</div>
</script>

有关编辑对话框的更多信息,请参阅此处

帮助文本

选择节点后,其帮助文本将显示在 info 选项卡中。这应该提供对节点功能的有意义的描述。它应该标识它对外发消息设置的属性以及可以对传入消息设置的属性。

第一个标签的内容在将鼠标悬停在 <p> 调色板中的节点上时用作工具提示。

1
2
3
4
5
6
7
8
9
10
<script type="text/html" data-help-name="node-type">
<p>Some useful help text to introduce the node.</p>
<h3>Outputs</h3>
<dl class="message-properties">
<dt>payload
<span class="property-type">string | buffer</span>
</dt>
<h3>Details</h3>
<p>Some more information about the node.</p>
</script>

此处提供了 node 帮助的完整样式指南。