無料ブログはココログ

[#Node-RED] 7〜8分でわかるファンクションノード

Photo

Visual IoTツール Node-REDには、データセットや基本的な制御構造がありますので、簡単なプログラムならコーディングなし(設定だけ)でプログラムを書くことができます。

しかし、少し複雑な処理になるとファンクションノードが便利です。ファンクションノードはjavascriptでコーディングできますので、複雑な処理をフローにまとめることができ、シンプルなわかり易いフローが書けます。

1.基本

まずは基本的な内容から。

1.1 名前をつけよう

ノードには日本語で名前を付けることができます。処理内容を表す名前を付けておくとフローが読み易くなります。「XXに変換する」「msg.cnt++」などわかり易い名前を付けてください。

1.2 入力は最大一つ、出力は複数可能

ファンクションノードの入り口は一つだけです。デフォルトの出力は一つですが、下部にある出力数を変更して増やすことができます。出力が複数場合は戻り値を配列で返します。出力しない出口にはnullをセットします。条件によってどれか一つに値を出力するだけなら、スイッチノードに渡した方が、修正が楽な場合もあるでしょう。
(追記:バージョン0.17以降では端子に名前をつけるとマウスオーバーで表示できますので、入出力などをわかり易く書いておくと良いでしょう。)

1.3 javascriptでコーディング

内蔵エディタはjavascript構文を理解してエラーやワーニングを表示してくれます。Node-REDが動作するNode.jsのバージョンの文法で記述します。msgオブジェクトを受け取って処理し、リターンバリューがmsgオブジェクトになります。

2.あらかじめ定義されたオブジェクト

msgオブジェクト以外のあらかじめ定義されたオブジェクトを説明します。

2.1 グローバルオブジェクト

プログラム全体で利用可能なグローバル変数が使えます。
global.get(‘オブジェクト名’)で取得し、 global.set(‘オブジェクト名’,値)で値を設定します。現在は永続化されないので、再起動で初期化されます。

2.2 フローオブジェクト

タブ内で有効なフロー変数もあります。
flow.get(‘オブジェクト名’)で取得し、flow.set(‘オブジェクト名’)で値を設定します。
再起動の他デプロイで初期化されますので、グローバル変数と使い分けてください。

2.3 node.send()

一つの出口に複数返す場合や、無名関数内で終了する場合は、returnでなく、 node.send(msgオブジェクト)を用います。returnは最後の一度死活変えませんし、無名関数内のreturnは全体の戻りではないからです。

2.4 require は setting.js で

ファンクションノード内で requireできません。requireが必要な場合は、ユーザーディレクトリ( ホームディレクトリ/.node-red)のsetting.jsのfunctionGlobalContextにrequireを追加します。Node-REDを再起動すると、設定したオブジェクトを global.get(‘オブジェクト名’) で取得できます。もちろん、必要に応じてユーザーディレクトリで、モジュールをnpmインストールする必要があります。

3.オブジェクトの管理

簡単に見えるNode-REDプログラミングですが、意外とハマるのがオブジェクトの管理です。

3.1 msg.payloadは基本

標準的な出力はmsg.payloadですが、破壊されることも多いです。payload以外のプロパティを使えば多くの場合は大丈夫です。しかし、新しいmsgオブジェクトが生成される場合は、フロー変数やグローバル変数を使います。

3.2 新しいmsgオブジェクトで返す

受け取ったmsgオブジェクトを渡したくない場合は、「return {payload: 値};」とすると新しいオブジェクトのmsg.payloadとして値を返すことができます。ただし、後続の処理で、受け取ったmsgオブジェクトの値を渡せないので注意してください。特にhttp inが出力するmsgオブジェクトには、http responseに必要な情報が含まれますので注意してください。

3.3 コピーされるオブジェクト

「return [msg, msg];」のようにmsgオブジェクトを複数同時に返すと、オブジェクトッがコピーされ、異なるオブジェクトが渡されます。また、一つの出口から複数のノードに繋いだ場合もコピーされます。大量データを扱う場合には注意してください。新しいmsgオブジェクトを生成するか、node.send()で出力すると参照が渡されます(参照を渡すと想定外に内容が破壊されてしまうことがありますので注意してください)。

4.おわりに

簡単に始められるNode-REDプログラミングですが、ドキュメントをきちんと読んだり、色々試さないとわからないことがあります。ここに挙げた内容がわかっていれば、少し複雑な処理もスラスラと書けると思います。

詳しくは、公式ドキュメント(英語)の確認や、実際に動かすなどしてください。それでも困った時は、ユーザー会slackやメーリングリストなど(英語)で聞いてみると良いと思います。

このエントリーをはてなブックマークに追加

Node-REDで品質の高いソフトウェアを開発する

開発者へのアンケートによると、Node-REDを用いると高品質なソフトウェアが開発できることがわかりましたが、そこには注意点がありました(Visual開発ツールNode-REDの導入によるプロセスの変化と考慮点 - SS2017 -)。

Node-REDで開発する際にはNode-REDに合わせた設計やテストが必要です。順に説明しましょう。

データ設計

Node-REDのノードは関数ではなく、msgオブジェクトを加工しながら処理を進めますので、msgオブジェクトのデータ構造をきちんと決めておかないと、混乱してバグの原因になります。

msgオブジェクトと同じ様に処理館の共通データであるフロー変数やグローバル変数、データベースも同様にデータ構造をきちんと決めておきます。

構造設計

フローをどのように構成するか、非同期並行処理(コールバック地獄よさようなら。Node-REDで非同期処理を使いこなせ! )か順次処理かを検討します。非同期処理は処理が速く効率的です。一方の順次処理はメモリーをあまり消費しないので、大量データを扱う際に有利です。メモリー消費が多いとシステムやDBノードが止まることもありますので、注意深く検討してください。

システム間連携

前回(Node-REDでより大規模な開発を - モジュール化とフロー・システム間連携 - )に書いた様に様々な連携方法があります。それぞれの長所短所を検討して方式を決めてください。

いずれの場合もなるべく早い時期から結合すれば、信頼性を高めることができます。

モジュール設計

いわゆるモジュール強度(凝集度)や結合度を考慮してモジュール化します。なるべく単機能のノードやサブフローになる様に、インタフェースのデータ構造が単純になる様に設計します。

また、ノードの出力するオブジェクトがが参照なのか、データのコピーなのか、あるいは新しいオブジェクトにするのかを検討します。新しいオブジェクトにすると、渡していないデータは破壊されます。特にhttpレスポンスには多くのデータが必要ですので注意してください。

逐次開発・テスト

いわゆるxunitはカスタムノードでないと利用できません。改造する際はデグレが生じ易いので気をつけてください。良いモジュール構造にして、逐次開発とテストを並行して行います。

少しずつ開発しながら随時テストすることで、信頼性の高いソフトウェアを増やしていきます。常に追加した部分を動作確認すれば、まとめてテストした場合よりも抜けが少ない様です(Visual開発ツールNode-REDの導入によるプロセスの変化と考慮点 - SS2017 -)。

機能テスト

機能テストを行う際は、機能の観点だけでなく、データの網羅性に注意すると良いでしょう。いわゆる界値に注目してテストデータを決めます。

Node-REDはスタブやモック、ドライバなど、いわゆるテストダブルの開発にも向いています。実機の利用が難しい場合に検討すると良いでしょう。特にDashboardを利用すると、使い易いテスト環境が実現できるでしょう。

非機能テスト

デバッグノードなどを不用意に繋ぐと、フローの二股に分かれたところでデータがコピーされ、メモリー負荷や、処理速度の低下に繋がるので注意してください。

Node-REDはNode.js上で非同期に動作しますので、スレッドによる実装よりも線形に性能が出ますが、残念ながら上限性能は存在します。小規模なテストで安心しないで、想定される負荷を実際に与えてテストしてください。

おわりに

Node-REDを使うと簡単に動くプログラムができます。しかし、ある程度規模が大きくなると、設計やテストの戦略が必要になります。

ここに挙げた方法は一例に過ぎません。開発対象や環境に合わせて実施してください。

このエントリーをはてなブックマークに追加

Node-REDでより大規模な開発を - モジュール化とフロー・システム間連携 -

Node-REDはマルチユーザ対応に向けてバージョンアップが進んでいますが、現在のエディタは基本的に一人でしか使えません。しかし、モジュール化やフロー・システム間連携を使えば、複数人で連携してより大規模な開発ができます。

1.  機能モジュールの作成方法

(1-1) エクスポート/インポート

エディタ上の任意のフロー/現在のタブ/全てのタブをJSONでエクスポートして人に渡したり、エクスポートしたJSONを受け取ってインポートすることができます(もちろんバックアップや複製にも使えます)。

エクスポートはフローを選択した後に、
 右上のハンバーガー(3本線)メニュー ⇒ 書き出し ⇒ クリップボード
を選択すると、 エクスポートのダイアログが表示されますので、選択したフロー/現在のタブ/全てのタブ、 それをコピーします。インデントのないJSONフォーマット/インデントのあるFOMAとを選択し、クリップボードに書き出します。

インポートは、
 右上のハンバーガーメニュー ⇒ 読み込み ⇒ クリップボード
を選択すると、 インポートのダイアログが表示されますので、現在のタブか新規のタブを選択して、エディタにコピーします。

(1-2) サブフロー

フローが大規模になると、同じフローが何度も出てくるなど見通しが悪くなりがちです。フローの一部をサブフローと呼ばれるモジュールを定義して扱うことができます。

右上のハンバーガーメニュー ⇒ サブフロー ⇒ サブフローを作成を選択すると、左ペインにサブフローが追加されサブフローのタブが開かれます。プロパティを編集するとサブフロー名や情報タブに表示される詳細情報を編集できます。入力(最大1つ)や出力(0以上)を設定すると、inputやoutputが表示されます。

またはサブフローにしたいフローの部分を選択しておいて、ハンバーガーメニュー ⇒ サブフロー ⇒ 選択部分をサブフロー化を選択するとその部分がサブフローになり、左ペインにサブフローが追加されます。左ペインのサブフローをダブルクリックするか、エディタ上のサブフローをダブルクリックして「フローのテンプレートを編集」をクリックすると、サブフローのタブが開かれます。

サブフローのタブを閉じてもサブフローは消えませんので、サブフローのタブを開いて削除します。

(1-3) カスタムノード

ユーザが独自のノードをJavascriptとHTMLで書くことができます。requireもできますので、様々なノードを 作ることもできます。 詳しくはCreating Nodesを見てください。

2. フロー・システム間連携

機能モジュール毎に担当者を分けて開発し、それらを以下のような方法でつなぎ合わせれば、より大きなシステムを作ったり、外部のシステムと連携することができます。

(2-1) デバッグノードの出力をインジェクトノードで送信

別の担当者のモジュールと接続する部分にデバッグノードをつないで、その出力のJSONをコピー(取り出)して担当者に渡します。担当者は受け取ったJSONをインジェクトノードに設定すると、異なるNode-RED間を直接つながないで、JSONデータを介してフローの続きを開発することができます。

(2-2) 異なるタブをリンクノードでつなぐ

機能ごとにタブを分けて、それぞれに担当者を決めておくとマージが容易になります。各タブのフローを繋いで大きなフローとするには、出力と入力のリンクノードを使います。

リンクノードは複数への出力や、複数からの入力の設定が可能です。リンクノードにわかりやすい名前をつけておくと良いでしょう。

(2-3) グローバル/フロー変数結合

取得したデータをメモリーやストレージに保存し、それを一定のタイミングで取得することも可能です。インジェクトノードで繰り返しを設定すると、一定間隔や指定日時、あるいはその組合せで処理を実行して、いわゆるポーリング処理やタスク処理を実現できます。

最も簡単な方法はグローバル変数やフロー変数を用いる方法です。グローバル変数は全てのノードから参照・更新が可能で、(いまのところ)Node-RED起動時に初期化されます。フロー変数は同じタブ(フロー)内のノードから参照・更新が可能で、デプロイ時に初期化されます。

(2-4) 処理間連携

httpノードやwebsocketノードを使えば、他のシステムと連携することができます。http inノードで要求を受けたら、同じ処理のフローにhttp responseノードを入れないと呼び出しが終了しないので気をつけてください。また、http requestノードは出力形式を文字列、バイナリバッファ、JSONを選ぶことができます。

他システムのデータをタスク処理やポーリング処理をするなら、ファイルやデータベースを使うと良いでしょう。ファイルは入出力のほかwatchノードで更新を検出することができます。また、カスタムノードをインストールすればPostgreSQLやSQLiteなどのデータベースを利用することができます。

3. おわりに

Node-REDで開発する際のモジュール化とフロー・システム間連携についてまとめました。

ここに挙げた方法を用いれば、複数人でNode-REDを用いてより大規模なシステムを開発することができます。みなさんも色々と工夫してください。

開発者へのアンケートによれば、大きなシステムを開発する場合はここにあげた方法だけでなく、設計やテストを工夫することで高品質なソフトウェアを短期間で開発することができます(Visual開発ツールNode-REDの導入によるプロセスの変化と考慮点 - SS2017 -)。設計やテストの工夫もいつかまとめたいと思います(Node-REDで品質の高いソフトウェアを開発する)。

このエントリーをはてなブックマークに追加

Node-REDから見えた未来 - 変わるもの、変わらないもの - SS2017 WG13

ソフトウェアシンポジウム(SS2017)では、前回紹介した論文発表のほか、ワーキンググループにも参加しました。

ワーキンググループでは各グループのテーマに沿って、参加者がそれぞれのポジションを発表して議論します。私が参加したのはWG13「ソフトウェア開発の現状と今後の発展に向けたディスカッション」で「Node-REDから見えた未来 - 変わるもの、変わらないもの -」を発表しました。

Node-REDは高機能なノード(モジュール)がたくさんあり、それらを組み合わせて高機能なシステムを効率的に開発できます。また、簡単にデバッグできるほか、デプロイが一瞬で、開発から確認の繰り返しを素早く実行できます。

このような環境を使っていると、面倒臭いことがなくなり、ソフトウェア開発に重要な作業を中心に実施する様になります。この重要なことはみなさん合意できますよね。という発表でした。

しかし、Node-REDのデモのインパクトが大きかったのか、Node-REDに対する質問で持ち時間が終わってしまいました。Node-REDを知ってもらえたので、良かったことにしておきます。

このエントリーをはてなブックマークに追加

Visual開発ツールNode-REDの導入によるプロセスの変化と考慮点 - SS2017 -

ソフトウェアシンポジウム2017(SS2017)で経験論文の発表をしてきました。経験論文とは研究論文の様に新規性はないものの、事例報告の様に経験を報告するものですが、問題設定や結果・考察を整理してより有効性や信憑性を高めて論文にまとめたものです。

今回はVisual IoTツールと呼ばれているNode-REDのアンケート結果を報告しました。ソフトウェア開発にツールは欠かせませんが、その導入報告はあまりありません。上流のツールであれば、コンサルタントに依頼することもできるかも知れませんが、下流のツールは小さい規模から始めることが多く、導入経験は他の人にも役に立つと思ったからです。

発表ではNode-REDの基本、長所・短所の説明と共にデモもしました。基本的な Hello World のノードを入れ替えてPathをセットするだけで、そのビジネスロジック(文字列の代入)をそのままWebサービスにできる様子をお見せしました。

このようにNode-REDは確認しながら開発するので短期間に品質の高いソフトウェアを作ることができ、アンケートにもある様に非同期処理が簡単に扱えます。その反面、ある程度の規模になれば、データやアーキテクチャなどの設計をきちんとしておかないと複雑になってしまいます。

そういった知識を持ち、ふさわしいプロセスで開発しないとうまくいかないことがアンケートからわかりました。まとめると

  • ツールの知識やノウハウを共有 する
  • 特性を活かした設計を行う
  • 実装を繰り返して常に確認する
  • 主体的にプロセスを変更し、品質 を上流から作りこむ

となり、これは、モダンアジャイル

  • 人々を尊重する
  • 安全な状態を前提とする
  • 素早い実験と学習
  • 価値を継続的に届ける

の基本理念と対応していて、Node-REDの良い導入が開発のアジリティ(機敏さ)を高めると考えられます。詳しくは以下の論文を読んでください。
(実は最終原稿の段階でモダンアジャイルの基本理念と対応していることに気付いたので追加しました)

Node-REDから見えた未来 - 変わるもの、変わらないもの - SS2017 WG13 につづく

このエントリーをはてなブックマークに追加

コールバック地獄よさようなら。Node-REDで非同期処理を使いこなせ!

Javascriptは非同期イベントドリブン処理が行える効率の良い処理系ですが、いわゆるコールバック地獄という見通しが悪くなる問題があります。Node.js上のビジュアル開発環境であるNode-REDは、機能モジュールであるノードをつなぐことで簡単に非同期処理を扱う事ができます。

ここでは、順次、 並列、待ち合わせといった基本的な処理をNode-REDでどのように書くかを説明します。

非同期イベントドリブン処理のイメージ

お湯を沸かしながらTVを見ることを考えてみます。やかんにお湯を入れて火をつけてアイロンを始めますが、アイロンに集中しすぎて空焚きになる事があります。

そこで、キッチンタイマーを1分ごとにセットして、タイマーがなるごとにチェックします。すると空焚きは防げますが、TVの内容がよくわかりません。マルチタスクは難しいのです。

では、やかんを笛吹きケトルに変えてみます。お湯が沸騰すると笛の音が鳴りますので、都合の良い所で火を消します。音が鳴るまでは気にせずにTVを見ている事ができます。

このように待ち時間がかかる処理の場合に、処理が終わるまでCPUを解放するのが非同期イベントドリブン処理です。タスク(計算機で言うとスレッド、プロセス、コンテナ)を定期的に切り替え無くても良いので効率的に計算機を使えます。

コールバック地獄

Javascriptはこのように効率的な非同期イベントドリブン処理の言語です。しかし、ケトルの笛にあたるコールバックを記述する際にネストが深くなるという欠点があります。

これは、コールバック地獄と呼ばれるもので、検索してみると多くの人が苦しんでいる様子が見えます。そこで、かつては async.js などの非標準ライブラリで順次処理や並列処理の待ち合わせが行われていました。

信じる者も救われるか微妙なPromise

Node.js v0.12 から Promise が標準で使えるようになりました。Promiseは処理の待ち合わせを実現するオブジェクトです。呼ばれた関数が先にオブジェクトを渡しておいて処理終了後にに状態を変える事で、オブジェクトを受け取った呼び出し側がコールバック処理に相当する次の処理が行えます。

Promise は良くできた仕組みで、コールバック地獄を無くします。しかし、少し複雑な処理を書こうとすると、Promiseアンチパターン - くじら公園 などを参考にウンウンうなる事になります。

また、Promiseはデータを受け渡す事ができますが、下流の方の処理に必要なデータを途中の処理すべてが受け渡さないといけません。全体の構造を考えたり、作り直すだけで疲れてしまいます。

そこでNode-RED

以前、このブログでも紹介した Node-RED はNode.js上で動作しますが、様々な処理を簡単に書く事ができます。

順次処理

ノードをつなぐと順次処理になります。前のノードの処理がreturnしたメッセージオブジェクトが、次のノードの入力となります。

Nr_5

結果:

Ans: 0

前段のファンクションノードの記述

msg.payload = 0; return msg;

同一データ個別並列処理

ノードの出力を複数のノードにつなぐと並列に実行されます。もちろん非同期ですので実行順序は保証されません(以降の並列処理も同じです)。

Nr_6

結果:

Ans: 0
答: 0

前段のファンクションノードの記述(新しいオブジェクトを作っていますが、順次処理の記述と効果は同じです。このフローでは問題ありませんが、msgオブジェクトの他のデータが引き継がれないので気をつけてください)

return {payload: 0};

個別データ個別並列処理

ノードの出力の数を増やすと、それぞれのデータが並列に実行されます。

Nr_7

結果

Ans: 0 答: 1

前段のファンクションノードの記述

return  [{payload:0}, {payload:1}];

個別データ同一並列処理(node.send)

ファンクションノードで node.send ()を使うと、複数のブジェクトを非同期出力できます。それぞれのデータが並列に実行されます。

Nrsend_2

結果

Ans: 0 Ans: 1 Ans: 2 Ans: 3 Ans: 4

前段のファンクションノードの記述

for (var i = 0; i < 5; i++) {     node.send({payload: i}); }

個別データ同一並列処理(split)

string, array, objectをsplitノードに渡すと、オブジェクトを分解してnode.send()できます(stringは区切り文字を指定できます)。インジェクトノードで配列を渡しています。

Nrsplit_2

結果

Ans: 0 Ans: 1 Ans: 2 Ans: 3 Ans: 4

待ち合わせ

splitノードで並列化した場合、joinノードで待ち合わせできます。msg.payloadのデータはまとめられます。

Nr_8

結果

[ "Ans: 0", "Ans: 1", "Ans: 2", "Ans: 3", "Ans: 4" ]

おわりに

Node-REDの様々な処理をまとめました。Node-REDは順次処理が基本になっていますので、javascriptに慣れていない方でも様々な処理を簡単に記述できます。

Node-REDはIoTの基盤として知られていますが、httpやwebソケットを含めて様々なプロトコルが扱えますし、Dashboardを使えば簡易なUIであれば簡単に作成できます。ちょっとしたテスト用のサーバなら、容易に構築できます。

Node-REDはJavascriptだからと敬遠されている方もおられるかも知れませんが、ぜひ一度、触ってみてください。

おまけ:非同期あるある

Node-REDのファンクションノードでAWS-SDKなどの非同期処理を呼ぶ場合、非同期処理のコールバック関数のreturnでは次のノードにデータを渡す事ができません。このような場合は、継続するノードが順次処理であってもnode.send()でデータを送ってください。

ちなみにAWS-SDKを呼ぶオーソドックスな方法は、settings.js内でrequireしたものをfunctionGlobalContextにセットしておいてファンクションノード内で利用します。

参考:https://nodered.org/docs/configuration#node-configuration

この記事は、SRA Advent Calendar 2016 の12月24日の記事です。

このエントリーをはてなブックマークに追加

カンタン非同期処理のNode-REDはデータ構造から始めよ!

node.js上で動くNode-REDのプログラムは ノードと呼ばれるモジュールをつなげることで非同期処理を簡単に記述することができます。

ここまでは簡単なプログラミングの説明をしてきましたが、今回は少し大きなプログラムを開発する場合を考えてみます。Node-REDでロジックを書く場合はファンクションノードにJavascriptで用なプログラミングします(カスタムノードを作ることもできます)。

Javascriptというといわゆる「コールバック地獄」(google検索)が連想されますが、Node-REDならノードを分けることで、快適にプログラミングできます。ここでは、なつかしのジャクソン法の命名にならって4つに分類してみましょう。

20160430_092923

基本

単純な処理は一つのファンクションノードで書くことができます。

図中のインジェクトノードからmsgオブジェクトを受け取って処理を行い、msgオブジェクトをreturnします。returnしたオブジェクトは次のデバッグノードでデバッグタブに出力されます。

連接

複数のノードを前後につなげます。処理1のノードがmsgオブジェクトをreturnすると、後続の処理2が実行されます。

全体としては非同期処理ですので、途中で外部呼び出しをすると、別の処理が開始されることがありますが、入力データの単位で見ると処理1と処理2は順次的に行われます。

選択

データに応じて処理を分ける場合、出力を複数に分けることができます。配列をreturnするとそれぞれが出力されます。出力が不要な場合は、その番号のところにnullをセットしておきます。

同じデータを渡すだけであれば1つの出力を複数のノードに渡すこともできます。
経験的には出力を複数にすると出力番号順、1つから複数に分けると後続のノードの作成順に実行される様に思いますが、保証されたものではありません。

反復

1つの入力から同じ処理を非同期に複数実行することもできます。node.send(obj)が呼ばれると処理中に、非同期に、後続処理が実行されます。

後続の処理は受け取ったオブジェクトをmsgとして処理しますので、returnで渡す場合と同じ様にnode.send()の引数もオブジェクトでないといけません。

ノード間のインタフェース

ノード間は関数呼び出しではないので引数がありません。Webプログラミングのセッション情報がmsgとして流れていくイメージが近いと思います。

msgはフロー(つまりタブ)をスコープとするFlow contextや全体で共通のGlobal contextと異なって、msg単位で独立しています。しかし引数と違って、ある処理が使わないデータが後続の処理で使われる場合もありますので、むやみに破壊することができません。

チェック&カスケード

安全なノードの基本構造はチェック&カスケードです。入力のmsgオブジェクトをチェックし、必要な処理を行い、受け取ったmsgオブジェクトをそのまま次のノードにカスケードします。

もちろん、msgオブジェクトに追加や変更をすることもありますが、受け取ったmsgオブジェクトを破壊しないことが基本です。

汎用性と保守性は違う

多くの言語では、汎用モジュールを参考にすると保守性の高いプログラミングが可能です。しかし、Node-REDでは注意が必要です。

Node-REDの標準ノードの多くはmsg.payloadをインタフェースにしています。しかし、始端あるいは終端となるノード以外でマネをすると、後続処理がmsg.payloadを退避する必要が生じてノード間の依存が強くなってしまい、保守性が悪くなります。

そこで、データの種類に応じてmsgオブジェクトにプロパティを追加する必要が出てきます。

データ構造から始めよ!

はじめに説明した様にNode-REDはファンクションノードによって、非同期処理を簡単かつ柔軟に扱うことができます。その反面、引数がないのでmsgオブジェクトを誤って破壊しない様に、データ構造をきちんと設計しておくべきです。

命名に用いたジャクソン法の4分類は、プログラムのデータ構造の分類です。それぞれに応じたフローがあるのですから、データ構造をどのようにするかを設計できれば容易に実装できるでしょう。

今回は全て配列をmsgにセットして渡しましたが、「選択」の場合は連想配列にすると見通しが良くなるかもしれません。このあたりは通信プロトコルなどの制約に応じて工夫してください。

おわりに

これまで説明してきた様にNode-REDは、設定中心のプログラミングが可能(Hello World!からNode-REDを知る)、通信に強い(RedmineのREST API を呼ぶ)、 ソーシャルに強い[Node-REDでTwitterのDMからRedmineのチケットを作成する)といった特徴がありますが、今回取り上げた非同期処理が簡単なことは、node.js上のNode-REDならではの特徴でしょう。

Javascriptだとコールバック地獄に落ち入りがちな順次処理が、フローをつなげることで簡単に実現できますし、データ構造にあわせた非同期処理も可能です。

書籍が出版されるなどIoT環境として注目されるNode-RED。ビジュアルでお手軽な点が強調されがちですが、注目されている理由はこの辺りにあるのかも知れません。

このエントリーをはてなブックマークに追加


[#Node-RED] Node-REDでTwitterのDMからRedmineのチケットを作成する

前回Node-REDからRedmineのREST API を呼んでチケット一覧を取得しましたが、今回はそれを少しずつ変更しながらちょっと実用的なものを作ります。

httpヘッダーをJSONでセット

前回は文字列としてヘッダーをセットしましたが、複数のヘッダーが必要ですのでJSONに変更します。チェンジノードのデータを「JSON {}」して以下の様にセットします。

{"X-Redmine-API-Key":アクセスキー}

一旦、デプロイしてインジェクトボタン(timestampと表示されているノードの左側の四角いボタン)を押して、右側のデバッグノードの確認します。

20160417_124531

上図のようにエラーが表示された場合は、表示されているノードの名前「APIキー」で確認できますが、エラーメッセージの上にマウスカーソルを移動させると対象のノードが赤の点線で囲まれるので簡単に確認できます。

httpヘッダーをJSONでセット

チケットを作成するにはPOSTで、 Content-Typeの設定が必要です。チェンジノードの設定を以下に変更して、確認します。

{"X-Redmine-API-Key":アクセスキー, "Content-Type":"application/json"}

プロジェクト作成&ID取得

Redmineで、プロジェクト -> 新しいプロジェクトと実行して、更新を受けるプロジェクトを作ります。さらに、プロジェクトを選んでから設定 -> メンバーの右側でユーザとロールを選んで追加しておきます。

プロジェクトIDはNode-RED呼び出しノードのURLをssues.jsonからprojects.jsonに変更して、インジェクトするとプロジェクトIDがわかります。

{ "projects": [ { "id": 3, "name": "自動投稿", "identifier": "social", "description": "ソーシャル投稿用のプロジェクトです", "status": 1, "created_on": "2016-04-17T04:01:39Z", "updated_on": "2016-04-17T04:01:39Z" }, { "id": 2, "name": "親プロジェクト", "identifier": ・・・

同様にステータスIDはissue_statuses.json、トラッカーIDはtrackers.json、優先度は/enumerations/issue_priorities.jsonで得られます。

POSTしてみる

Redmineのドキュメントを参考にチケットを作ってみます。入力をSubjectに入れるので、チェンジノードをあきらめて、ファンクションノードに以下のコードを設定します。

msg.headers =
{"X-Redmine-API-Key": アクセスキー, "Content-Type":"application/json"};
msg.payload =
{
  "issue": {
    "project_id": 3,
    "tracker_id": 4,
    "subject": msg.payload,
    "priority_id": 2
  }
};
return msg;

入力がtimestampでは寂しいので「漢字のサブジェクト」という文字列をインジェクトノードにセットします。設定画面のpayloadで「aZ」を選択して設定します。

デプロイしてdebugタブに以下のような表示がされれば成功です。

{ "issue": { "id": 17, "project": { "id": 3, "name": "自動投稿" }, "tracker": { "id": 4, "name": "タスク" }, "status": { "id": 1, "name": "新規" }, "priority": { "id": 2, "name": "通常" }, "author": { "id": 1, "name": "阪井 誠" }, "subject": "漢字のサブジェクト", "start_date": "2016-04-17", "done_ratio": 0, "spent_hours": 0, "created_on": "2016-04-17T05:57:57Z", "updated_on": "2016-04-17T05:57:57Z" } }

twitter対応

チケット作成が確認できたので、いよいよtwitterに対応しましょう。Node-REDの左側の少し下の方にtwitterノードがありますのでこれをクリックします。すると右側のinfoタブに説明が表示されます。すると、送信者はtopic、内容はtweetにセットされることがわかります。

そこで、テストデータとしてインジェクトノードのtopicに送信者、payloadに漢字の概要を設定して、チェンジノードでmsg.payloadをmsg.tweetにセットします。

Redmineを確認すると、題名が「送信者」、説明が「漢字の説明」となったチケットができているはずです。

twitterノードの追加

入力としてtwitterノードを追加します。「Add new twettier-credentials...」の状態で右のペンのアイコンをクリックするとtwitterの連携用の認証に進むことができます。searchはダイレクトメッセージを選択します。

デプロイして自分にダイレクトメッセージすると、説明にプロフィール等が載っていてそのままではチケットの説明に使えません。msg.tweet.textが求めている送信文でした。最終的にファンクションノードは以下の様なコードになります。インジェクトも同じ様に変更します。

msg.headers =
{"X-Redmine-API-Key":アクセスキー, "Content-Type":"application/json"};
msg.payload =
{
  "issue": {
    "project_id": 3,
    "tracker_id": 4,
    "subject": msg.topic,
    "description": msg.tweet.text,
    "priority_id": 2
  }
};
return msg;

ようやくチケットができました。

20160417_154511

ふりかえり

Node-REDを使って段階的に開発してみました。インタフェースがJSONで統一されているので、インジェクトノードで下流を確認しておいて、あとから実際の通信をすることができます。

時差意の処理はファンクションノードで頑張れば大抵のことはできるのですが、細かく分けて実装するとdebugタブのエラー表示をうまく使えます。

また、infoタブで仕様の確認ができるので、インタフェースを確認しながら作り上げることができます。もし、勘違いがあればdebugノードを追加して簡単に確認できます。

20160417_171132

作成したフロー中のデバッグノードには、右側の四角の部分が白いものがあります。これはデバッグに使ったもので、そのままでは煩わしいので表示を停止したものです。再デプロイしなくても四角の部分を押すだけで変更できます。

まとめ

IoT環境として知られているNode-REDは、通信プログラムが作り易い環境です。

ソフトウェア開発に通信がからむと結合時に実物との擦り合わせが必要です。twitterノードのポーリングは15分に1回ですので、今回の様に段階的に開発しないと開発にとても時間がかかるでしょう。

Node-REDユーザに公開されているノードには、この他にもソーシャネット向けのノードがたくさんあり、無料サービスのFREDでもたくさんサポートされています。

今回はイベントドリブンな方法のみを紹介しましたが、インジェクトノードはcronの様な定期実行も可能です。色々なシステムを作ることが可能でしょう。

ぜひ、一度Node-REDに触れてみてください。
(注:今回利用したtwitterノードはデータ取得を100%保証していないので、実際に使う場合は別の方法でもチェックしてください)

おまけ:Redmineの視点で

今回は前田さんの発表を参考に、メールの代わりにTwitterを入力源として使ってみました。もちろん、文字コードの考慮が必要ですが、RedmineのメールをチェックしてTwitterで通知することも簡単にできます(こちらはデータ抜けがないでしょう)。

さらに応用すれば、社内用とプロジェクト用のRedmine間で更新をリンクさせることも可能になるでしょう。チケット作成時にチケット番号が返ってきますので、それを何かに覚えておけば容易に実現できるでしょう(Node-REDはDBなどのストレージも色々使えます)。

次回は、もう少し大きなシステムを作る際に考えないといけない保守性について書いてみたいと思っています。

このエントリーをはてなブックマークに追加

[#Node-RED] RedmineのREST API を呼ぶ

前回は「Hello World!」のWebサーバを構築しましたが、今回はクライアントとしてNode-REDからRedmineのREST APIを呼んでみます。なおNode-REDとRedmineはローカル環境で動作させました。

20160414_233706

フローの説明

グレイ(青?)のインジェクトノードの左の四角いボタンを推すとタイムスタンプがmsg.payloadに入れられます(中身は使わずタイミングを与えています)。

  1. チェンジノードでmsg.headers(httpヘッダー情報)に「X-Redmine-API-Key=アクセスキー」をセットします。
  2. httpリクエストノードでhttp://Redmineサーバアドレス/redmine/issues.jsonを呼び出(GET)します。
  3. JSONが一つの文字列でmsg.payloadにセットされるのでファンクションノードでオブジェクトに変換します。
  4. デバッグノードでデバッグタブに出力します。

JavaSscriptはたった2行

なお、JavaScriptのコードはファンクションノードに書いた以下の2行だけです。

msg.payload = JSON.parse(msg.payload);
return msg;

Redmineの設定

REST API を有効にする:
管理 -> 設定 -> 認証で、「RESTによるWebサービスを有効にする」をチェック

アクセスキーの取得:
個人設定-> APIアクセスキー(右側に表示されます) の「表示」を押すとAPIアクセスキーが表示されます。必要に応じてリセットしてください。

認証が必要:
管理 -> 設定 -> 認証で「認証が必要」のチェックをしている場合は、ベーシック認証のユーザとパスワードを設定します。この場合はアクセスキーは不要な様です(Redmine 2.5.2で確認しました)。

注意点

デバッグ情報を全て表示したい場合は、REST APIのパラメータでlimitやoffsetを指定してください。途中で表示が切れる場合はNode-REDのsetteings.jsでdebugMaxLengthを変更してください。

おわりに

Node-REDなら、ちょっとした処理をとても簡単に実現できます。今回はインジェクトノードのボタンを押すことで実行しましたが、http、Twitter、メール、Websocket、tcp、udp、MQTT、シリアル、ファイル更新、などを入出力とする事ができます。

このように入力や出力を簡単に取り替えられるのは、ノード間のインターフェースがJSONで標準化されていることのほか、インジェクトノードやデバッグノードが良くできているので、作り易いからだと思います。

次回はその辺りを意識しながら説明してみようと思います([#Node-RED] Node-REDでTwitterのDMからRedmineのチケットを作成する)。

おまけのコード

ローカル環境やFREDでインポートできます(2016/4/18 追記:無料で利用できるのは72時間までになりました)。動かすには、サーバーアドレスとアクセスキーを変更する必要があります。ちなみにローカルIPアドレスですので、攻撃はできません。

このエントリーをはてなブックマークに追加

[#Node-RED] Hello World!からNode-REDを知る

Node-REDはIoT向けのビジュアルツールで、データフローを定義することでプログラミングできます。IBMのEmerging Technologyというチームが開発し、オープンソースで公開されているほか、Bluemixでサポートされています。

node.jsが動作する環境で利用することができますが、体験するだけならFREDにアカウントを作れば無料で色々と開発できます。今回はFREDを使って説明します。
(2016/4/18 追記:無料で利用できるのは72時間までになりました)

まずはHello World!

Node-REDはノードと呼ばれるモジュールを組み合わせて、フローと呼ばれるプログラムを構成します。

ノードには標準のものと公開されているもののほか、カスタムノードがあります。独自のロジックを作るには標準のファンクションノードにJavascriptのプログラムを書くか、Javascriptとhtmlでカスタムノードを作ることになります。

最もシンプルなプログラムは、標準ノードを組み合わせて設定するだけでできます。左側にあるノードをドラッグ&ドロップして配置し、各ノードの丸と丸の間をつなぐだけです。いわゆる「Hello World !」は以下の様になります。

20160410_235702_2

図の上部に3つ直線に並んでいるものをノードという基本的な処理で、最初のノードが「http in」というノードでURLとメソッド「GET」を設定しています。中央のチェンジノードはmeg.payloadに「こんにちは」という文字列を設定しています(ダブルクリックで設定できます)。最後の「http response」ノードは受け取ったmeg.payloadを返します。

たったこれだけの設定で右上の[Deploy]ボタンを押すだけでwebアプリを構築でき、設定したURLにブラウザでアクセスすると、「こんにちは」と表示されます。

(FREDはマルチユーザ対応していますのでURLに特別なルールがあります。ユーザ・ドキュメントを確認してください)

どこでもダンプ& 簡単結合

「msg」と表示さているノードはデバッグノードで、右側のdebugタブに設定したオブジェクトが表示されます。設定によってmsgオブジェクトの一部を表示することもできます。

ノード間はmsgオブジェクトが受け渡されますので、 任意のところに接続するとその全てをダンプできます。これは分割して開発している場合にとても便利です。

先行する処理の出力を、後続する処理の入力として「inject」ノードから与えると、別の拠点で開発していても、事前に結合の確認ができます。

インポートで共有

分散開発にはインポート/エクスポートも便利です。プログラムはJSONファイルですので、右上のハンバーガー(三本線メニュー)-> Import -> Clipbord でペーストすると、他の人の開発したプログラムを読み取ることができます。

例えば GUIでカンタン開発!Node-REDでズンドコキヨシ on @Qiita の下の方にあるJSONをコピーし、上記の方法でインポートしてデプロイすると、すぐにズン!ドコ!キヨシ!を実行できます。

人に渡す場合は、対象のノードを選んでおいて、 右上のハンバーガー(三本線メニュー)-> Export -> Clipbordで表示できますので、コピーして利用します。

最初に示したHello World !はこんな感じになります。

これをFREDでインポートして、https://fred.sensetecnic.com/public/<アカウント名>/hello でアクセスすると「こんにちは」と表示されます。

Node-REDでシステムを開発する際に注意すべき汎用性と保守性について説明する予定でしたが、次回以降もRedmineの紹介をもう少しする予定です( [#Node-RED] RedmineのREST API を呼ぶ)。

このエントリーをはてなブックマークに追加