LoginSignup
33
28

More than 5 years have passed since last update.

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

Last updated at Posted at 2015-12-06

公式ドキュメントにあるNode作成の基本であるCreating your first nodeを読み解きながら実際に試して詳細まで把握します。

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

これは、そのままですが補足記事は以下になります。
* nodeDirについてはこちら
* NPMについてはこちらなど

シンプルなNode

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

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

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関数によって定義されます。

これは先ほどREDconsole.logで中身を見る際に注意書きしましたが、ワークスペースにNodeがプロット(配置)されていない状態ではConsoleに何も表示されない、つまりインスタンス化されていないということになります。

では以下のようにNodeを3つ配置してみたらどうでしょうか?

スクリーンショット 2015-12-06 14.30.55.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設定を見てみると以下のようになってます。

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のwires5c0d29a1.a3f2d8というIDが指定されていて、それが後続のlower-case Nodeのidだったりします)

最後にLowerCaseNodeは小文字に変換したメッセージをnode.sendを利用して後続に送り出します。

ここで、var node = thisとして一旦thisnodeという変数に代入しています。これを理解するために以下のようにinputイベントリスナの関数内でthisconsole.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にメッセージを流す)してみます。

スクリーンショット 2015-12-06 23.25.16.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としています。

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

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

33
28
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
33
28