公式ドキュメントにあるNode作成の基本であるCreating your first nodeを読み解きながら実際に試して詳細まで把握します。
NodeはNodeの設定を編集するダイアログを定義するHTMLファイルとNodeの動作を定義するJavaScriptファイルの2つのファイルで構成されます。これらのファイルは、nodesディレクトリまたはnodesDirの設定で定義されたディレクトリのいずれか配置します。また、NodeはNPMで管理することもできます。
これは、そのままですが補足記事は以下になります。
シンプルなNode
次の例は流れてくるメッセージのうちアルファベットをすべて小文字に変換する動きをします。
module.exports = function(RED) {
function LowerCaseNode(config) {
RED.nodes.createNode(this,config);
var node = this;
this.on('input', function(msg) {
msg.payload = msg.payload.toLowerCase();
node.send(msg);
});
}
RED.nodes.registerType("lower-case",LowerCaseNode);
}
Node.jsでは
module.exports
に関数を代入することで外部からアクセスできるようになります。また、関数はRED
引数によってNode-REDの様々な機能へアクセスすることができます。
Node.jsでのモジュール定義は[NodeJS] モジュール定義について学ぶなどが詳しいです。RED
については難しく考えないで以下のようにconsole.log
で中身見てみましょう(ワークスペースにlower-case Nodeをプロットしていないと何も表示されません)
module.exports = function(RED) {
function LowerCaseNode(config) {
console.log(RED); // <- 追加
RED.nodes.createNode(this,config);
var node = this;
this.on('input', function(msg) {
msg.payload = msg.payload.toLowerCase();
node.send(msg);
});
}
RED.nodes.registerType("lower-case",LowerCaseNode);
}
以下のようにRED
のプロパティがConsoleに表示されるので眺めてみるとNode-REDを操作できる色々なものが詰め込まれているのがわかります。Nodeを作るとはRED
を利用してPluggableにNode-REDを拡張するということがよくわかります。
{ init: [Function],
start: [Function: start],
stop: [Function: stop],
nodes:
{ init: [Function: init],
load: [Function: load],
createNode: [Function: createNode],
getNode: [Function],
eachNode: [Function],
addFile: [Function: addFile],
addModule: [Function: addModule],
...
Nodeは新しいインスタンスが作成されるたびに呼び出される
LowerCaseNode
関数によって定義されます。
これは先ほどRED
をconsole.log
で中身を見る際に注意書きしましたが、ワークスペースにNodeがプロット(配置)されていない状態ではConsoleに何も表示されない、つまりインスタンス化されていないということになります。
では以下のようにNodeを3つ配置してみたらどうでしょうか?
3つ分プロパティが表示されたと思います。これで解るのはNodeがパレットに配置されているだけでは上記のJSファイルで定義したNodeの動作はインスタンス化されていないということです。
最初にすべてのNodeでの共有機能を初期化するために
RED.nodes.createNode
を呼び出します。その後、Node固有のコードを記述します。この例ではNodeはNodeにメッセージが到着するたびに呼び出されるinput
イベントにリスナーを登録します。このリスナーの中でメッセージを小文字に変換し、後続のフローに変換したメッセージを渡すためにthis.send
を呼び出します。
関数の最初でRED.nodes.createNode
を呼び出すのは必須のようです。では、RED.nodes.createNode
を呼び出した後のthis
がどういう状態なのか以下のようにconsole.log
を追加して見てみます。
module.exports = function(RED) {
function LowerCaseNode(config) {
RED.nodes.createNode(this,config);
console.log(this); // <- 追加
var node = this;
this.on('input', function(msg) {
msg.payload = msg.payload.toLowerCase();
node.send(msg);
});
}
RED.nodes.registerType("lower-case",LowerCaseNode);
}
Consoleには以下のように表示されます。
{ id: '5c0d29a1.a3f2d8',
type: 'lower-case',
z: 'f56b199.f0a94e8',
_closeCallbacks: [],
wires: [ [ '17d78d74.e82873' ] ],
_wireCount: 1,
send: [Function],
_wire: '17d78d74.e82873' }
{ id: '112248a6.eeddb7',
type: 'lower-case',
z: 'f56b199.f0a94e8',
_closeCallbacks: [],
wires: [ [ 'b308844.f4cf778' ] ],
_wireCount: 1,
send: [Function],
_wire: 'b308844.f4cf778' }
{ id: 'f8fc3c36.0703c',
type: 'lower-case',
z: 'f56b199.f0a94e8',
_closeCallbacks: [],
wires: [ [ 'b2266937.4dd998' ] ],
_wireCount: 1,
send: [Function],
_wire: 'b2266937.4dd998' }
一方、flow設定を見てみると以下のようになってます。
{"id":"1fce0738.e031f9","type":"inject","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":112,"y":65,"z":"f56b199.f0a94e8","wires":[["5c0d29a1.a3f2d8"]]},
{"id":"5c0d29a1.a3f2d8","type":"lower-case","name":"","x":274,"y":71,"z":"f56b199.f0a94e8","wires":[["17d78d74.e82873"]]},
{"id":"17d78d74.e82873","type":"debug","name":"","active":true,"console":"false","complete":"false","x":489,"y":74,"z":"f56b199.f0a94e8","wires":[]},
{"id":"af2b7be1.50d488","type":"inject","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":119,"y":132,"z":"f56b199.f0a94e8","wires":[["112248a6.eeddb7"]]},
{"id":"112248a6.eeddb7","type":"lower-case","name":"","x":281,"y":138,"z":"f56b199.f0a94e8","wires":[["b308844.f4cf778"]]},
{"id":"b308844.f4cf778","type":"debug","name":"","active":true,"console":"false","complete":"false","x":496,"y":141,"z":"f56b199.f0a94e8","wires":[]},
{"id":"f452540.f0badb","type":"inject","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":113,"y":203,"z":"f56b199.f0a94e8","wires":[["f8fc3c36.0703c"]]},
{"id":"f8fc3c36.0703c","type":"lower-case","name":"","x":275,"y":209,"z":"f56b199.f0a94e8","wires":[["b2266937.4dd998"]]},
{"id":"b2266937.4dd998","type":"debug","name":"","active":true,"console":"false","complete":"false","x":490,"y":212,"z":"f56b199.f0a94e8","wires":[]}
つまり、RED.nodes.createNode
によって設定ファイルに従ってでNodeが初期化されているのがわかります。id
は各Node固有のIDでz
はNode TypeのIDを表しているようです。
面白いのがwires
プロパティで上記フローのinject -> lower-case -> debugをワイヤーでつないでいる様を表しています(例えばinject Nodeのwires
に5c0d29a1.a3f2d8
というIDが指定されていて、それが後続のlower-case Nodeのid
だったりします)
最後にLowerCaseNodeは小文字に変換したメッセージを
node.send
を利用して後続に送り出します。
ここで、var node = this
として一旦this
をnode
という変数に代入しています。これを理解するために以下のようにinput
イベントリスナの関数内でthis
をconsole.log
してみます。
module.exports = function(RED) {
function LowerCaseNode(config) {
RED.nodes.createNode(this,config);
var node = this;
this.on('input', function(msg) {
console.log(this); // <- 追加
msg.payload = msg.payload.toLowerCase();
node.send(msg);
});
}
RED.nodes.registerType("lower-case",LowerCaseNode);
}
Consoleに何も表示されないですね。イベントが発生していないからです。
では、以下のinject Nodeのスイッチをクリックしてイベントを発生(lower-case Nodeにメッセージを流す)してみます。
this
の中身は以下のように_events
プロパティが追加されました。
{ id: '5c0d29a1.a3f2d8',
type: 'lower-case',
z: 'f56b199.f0a94e8',
_closeCallbacks: [],
wires: [ [ '17d78d74.e82873' ] ],
_wireCount: 1,
send: [Function],
_wire: '17d78d74.e82873',
_events: { input: [Function] } }
つまり、イベントリスナの関数内でのthis
と区別する為にvar node = this
として、イベントリスナの関数内でsend
関数を利用する際はnode.send
としています。
<script type="text/javascript">
RED.nodes.registerType('lower-case',{
category: 'function',
color: '#a6bbcf',
defaults: {
name: {value:""}
},
inputs:1,
outputs:1,
icon: "file.png",
label: function() {
return this.name||"lower-case";
}
});
</script>
<script type="text/x-red" data-template-name="lower-case">
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<script type="text/x-red" data-help-name="lower-case">
<p>A simple node that converts the message payloads into all lower-case characters</p>
</script>
NodeのHTMLファイルにはEditor上でのNodeの設定を行う編集ダイアログやヘルプテキストを定義します。この例ではNodeにユーザが任意で付与できる
name
プロパティがあります。nameプロパティはワークスペース上の複数の同Nodeを区別するために広く使用されている慣習があります。
ここは特に説明不要かと思います。