Node-REDのNode作成はじめの1歩

公式ドキュメントにあるNode作成の基本である[Creating your first node](http://nodered.org/docs/creating-nodes/first-node.html)を読み解きながら実際に試して詳細まで把握します。

> NodeはNodeの設定を編集するダイアログを定義するHTMLファイルとNodeの動作を定義するJavaScriptファイルの2つのファイルで構成されます。これらのファイルは、nodesディレクトリまたはnodesDirの設定で定義されたディレクトリのいずれか配置します。また、NodeはNPMで管理することもできます。

これは、そのままですが補足記事は以下になります。
* nodeDirについては[こちら](http://qiita.com/joeartsea/items/b5e8e14498f3098990c3#nodesdir)
* NPMについては[こちら](http://qiita.com/yoh-nak/items/8446bf12094c729d00fe)など

## シンプルなNode

>次の例は流れてくるメッセージのうちアルファベットをすべて小文字に変換する動きをします。

```js:lower-case.js
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] モジュール定義について学ぶ](http://www.yoheim.net/blog.php?q=20150101)などが詳しいです。`RED`については難しく考えないで以下のように`console.log`で中身見てみましょう（ワークスペースにlower-case Nodeをプロットしていないと何も表示されません）

```js:lower-case.js
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つ配置してみたらどうでしょうか？

<img width="588" alt="スクリーンショット 2015-12-06 14.30.55.png" src="https://qiita-image-store.s3.amazonaws.com/0/21109/b357e61c-5500-0573-e926-a27dd636f348.png">

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設定を見てみると以下のようになってます。

```json:flows_hostname.local
{"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にメッセージを流す）してみます。

<img width="904" alt="スクリーンショット 2015-12-06 23.25.16.png" src="https://qiita-image-store.s3.amazonaws.com/0/21109/81d0ac79-f196-cc94-4da2-93e983a51d0b.png">

`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`としています。

```html:lower-case.html
<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を区別するために広く使用されている慣習があります。

ここは特に説明不要かと思います。