ノード間のデータの受け渡し(Context)

1.概要

Node-REDは、基本的に前段からメッセージが送られてくると処理を行い、結果を次段にメッセージとして渡すという処理を繰返して、プログラムが動作していきます。
この場合、あるノードに前段からデータ何回遅れてきたかをカウントする場合には、どのようにしたらよいでしょうか。
今回メッセージが来たのですから、今までメッセージが来た回数に今回の分1を足せば回数は分かります。しかし、今まで来た回数をどこに保管できるのでしょうか。
言い換えれば、過去のノードと現在のノードとの間で、今まで来た回数を受け渡す方法が必要になります。
Node-REDには、このように過去ノードと現在のノード間のようなノード間でデータを渡す手段としてcontextという手法があります。
contextは、データが渡せる範囲(スコープ)によって、node, flow, globalの3種類があります。

  • ノード – 値を設定したノードのみアクセスできます。
  • フロー – 同じフロー(またはエディタのタブ)のすべてのノードがアクセスできます。
  • グローバル – すべてのノードがアクセスできます。

ここでは、node-contextとflow-contextを使って、1秒ごとに0~9までカウントするカウンタを作ってみてcontextの説明を行います。
2つの方法で構成してみます。functionノードを使わない方法とfunctionノードを使う方法です。

2. カウンタのフロー(functionノードを使わない方法)

functionノードを使わない方法で、0~10のカウンタのフローチャートは、下の図のようになります。この場合は、flow-contextを使います。
flow-contextは、flow.countです。flow.countは、カウント値を保存します。
change-1,change-2,change-3の3つのノードで参照もしくはセットされます。

 

フローチャートの動作を説明します。
(1) flow.countはデプロイ直後、inject-1とchange-1で0にリセットされます。
(2) その後,inject-2が1秒毎にメッセージを発信します。
(3) そのメッセージを受けて、change-2でflow.countをmsg.payloadにセットします。msg.countとflow.countにmsg.payloadをインクリメントした数値をセットします。
(4) msg.payloadをdebug-1で表示します。
(5) msg.countが10以上(switch-1)であれば、flow.countを0にリセット(change-3)します。

このように、flow-contextは、ノード間で値を共有できます。
具体的な、Node-REDのフローは以下になります。フローチャートのノード記号を追記しています。change-1とchange-2に相当するchangeノードのプロパティを以下に示します。flowは、msgの代わりに選択できます。

3 カウンタのフロー(functionノードを使う方法)

functionノードを使用すると、node-contextだけで同じカウンタが実現できます。ただし、javascriptのプログラムを記述しなければなりません。
functionノードを使う方法の場合のフローチャートを次に示します。
contextは、node-contextの’count’です。function-1の初めで、読み出して最後に書込んでいます。この動作で次の1秒後にデータを渡せます。
途中では、countをmsg.payloadにセットし、countをインクリメントします。
その後、countが10以上であれば、countを0にリセットします。
そして、node-contextの’count’にcountを書き込みます。
その後、debugノードでmsg.payloadを表示します。

このように、node-contextは、一つのノードの未来のノードとデータを共有できます。
具体的な、Node-REDのフローは以下になります。フローチャートのノード記号を追記しています。functionノードのプロパティも示します。

// もしまだカウンタが存在していなければ0に初期化
var count = context.get('count') || 0;
msg.payload = count;
count += 1;
if (count >= 10){
    count = 0;
}
context.set('count', count);
return msg;

Functionノードは複数回の呼び出しの間で、自分のコンテキストオブジェクトの中にデータを保持しておくことができます。そして、コンテキストにアクセスすることができる以下の3つの定義済み変数があります。:

context – ノードスコープのローカルなコンテキスト
flow – フロースコープのコンテキスト
global – グローバルスコープのコンテキスト

そして、それぞれのAPIは以下の様になります。

context
 context.get(..) : ノードスコープコンテキストからプロパティ値を取得する
 context.set(..) : ノードスコープコンテキストにプロパティ値を設定する
 context.keys(..) : すべてのノードスコープコンテキストのプロパティキーの一覧を取得する
 context.flow : flowと同義です
 context.global : globalと同義です

flow
 flow.get(..) : フロースコープコンテキストからプロパティ値を取得する
 flow.set(..) : フロースコープコンテキストにプロパティ値を設定する
 flow.keys(..) : すべてのフロースコープコンテキストのプロパティキーの一覧を取得する

global
 global.get(..) : グローバルスコープコンテキストからプロパティ値を取得する
 global.set(..) : グローバルスコープコンテキストにプロパティ値を設定する
 global.keys(..) : すべてのグローバルスコープコンテキストのプロパティキーの一覧を取得する

 

■ 上記カウンタのfunctionの記述の別の例

// もしまだカウンタが存在していなければ0に初期化
if (!context.count) {
    context.count = 0;
}
msg.payload = context.count;
context.count += 1;
if (context.count >= 10){
    context.count = 0;
}
return msg;

4 その他の例

PDHのファイル書込みの開始/停止にも”flow.rec_enable”というcontextを使用しています。
こちらのページをご覧ください。「データの保存と読み込み

また、こちらのページ「一定時間継続の閾値超えの検出」では、flow.limit, flow.limitOver, flow.detectEnableという3本のflow-contextを使用して一定時間継続の閾値越えを検出するという機能を実現しています。