はじめに
やりたいこと
- Node-REDでどんなことがどこまでできるのかを調べる
- part6(TIPS編)では、実装を作っている中でみつけた小技を紹介します。
Node-REDのまとめ記事
動作環境
- OS : CentOS Linux release 7.5.1804 (Core)
- Node-RED : v0.19.5
- docker : 18.09.1
- Node-REDイメージ : nodere/node-red-docker (IMAGE ID/d4f07b39b24d)
- Dockerを使いますが、ホストに立てても大丈夫かと思います。
免責事項
- 検証をして確実と思われる情報を載せておりますが、誤っている可能性もゼロではないので、参考程度にご利用ください
- 本来はコードと実行結果のコンソールログも載せるべきなのですが、数と量が多いので、一旦は画面イメージまでとさせてください。
調査結果
メタフィールドの活用
- 一般的にフローの中で情報を共有したい場合は、flowコンテキストを使う
context.get("hoge")
ただし、これでは問題になるパターンがある
- フローコンテキストは多重処理や再実行時に共有されて、占有して使いたいときは、排他制御が必要
- つまり、先行タスクが値を設定し取得する前に、後続タスクが値を書き換えてしまうと矛盾が起きる
- サブフローからは、参照ができない。
- v0.2からはできるらしいが、現状最新版ではリリースされていない
そこで、無難にmsgオブジェクトのメタフィールドを使う
functionノード
msg._id = "user1";
msg._type = "admin";
return msg;
- そもそもmsgオブジェクトをノード間で引き渡していくのがNode-REDの思想なので、合致する
-
return null
とするとフローが終了する
-
- msg.payloadは頻繁に書き換えられるが、メタフィールドが書き換えられることはない
- JSONノードなどで変換した際に、メタフィールドは含まれないので、使いようによっては便利
セッションの異なるWebSocketへの出力
- WebSocketを異なる接続先に出力させる場合は、
msg._session
の削除が必要- 入力用WebSocketから信号がきたら、出力用の別のWebSocketに信号を出すなど
functionノード
delete msg._session
return msg
- ちなみに、エラーが出ずただ信号が届かない状況になるので、すこし切り分けに焦るw
コンテキストの削除
- contextにはget,set,keysしか定義されておらず、情報を消すことができない
- context.set("hoge",null)としてもダメ
- 偶然発見したが以下で消せる
- setterに値を入れず、キーだけを指定すると、エラーにならず登録を消せる。
- API仕様にも記述がないし、裏機能かバグっぽいきがする。。。
functionノード
context.set("hoge")
## WatchDog
- タイムアウトを見張りたい
- 待ち合わせではなく、別経路が途中で止まったり極端に遅延した場合にWarningを出したいという意図
- 単に次の信号が来たら時間を延長させるという意味ではない
- 信号A:0.3s
- 信号B:5.2s // こいつを見つけたい
- 信号C:0.4s
- これらの信号は、多重であったりしばらく期間が空くこともあるとする
Functionノード
var ID = msg._msgid.replace(".","")
var timeout = context.get(ID);
var flag = 0;
//正規ルート(先のはず)
if(msg.payload === "PASS"){
// 正規ルートが先だった
if(timeout !== "TIMEOUT"){
context.set(ID,"PASS")
flag = 0;
//タイムアウトが先だった(追いついた)
}else{
//変数を消しておく
//第2引数を省略するとオブジェクトが消える
context.set(ID)
flag = 2;
}
}
//タイムアウトルート(後のはず)
if(msg.payload === "TIMEOUT"){
// タイムアウトが先に来た
if(timeout !== "PASS"){
context.set(ID,"TIMEOUT")
flag = 1
// timeoutが後だった
}else{
//変数を消しておく
flag = 0;
//第2引数を省略するとオブジェクトが消える
context.set(ID)
}
}
if (flag === 1){
msg.payload = "timeoutが発生しました"
return msg;
}else if(flag === 2){
msg.payload = "timeoutに追いつきました"
return msg;
}else{
return null;
}
- msgオブジェクト(msg._msgid)が同じである仮定の下、ノードオブジェクトに文字列を入れることで確認
- msgオブジェクトには「.」が含まれているので、除いています
- 実装例では、正規ルート側はPASS、タイムアウト側はTIMEOUTを文字列としていれて、コントロールしています
もっと簡単なやりかたがある気がする。。。
任意箇所からの出力
- ログ出力などを集約しいて表示させたい
- 仮想リンクでもよいが、サブフローから使えないのが問題
- 仮想リンクには名前を付けられない
- ログ出力する側のfunctionノードに名前を付け忘れました。すみません。
ログ出力する
global.set("LogSend",node.send)
ログ出力させる側
global.get("LogSend")(msg)
-
TESTから延びるノードはdebugノードにつながっていないが、ログ出力ノードから信号を出して、メッセージを出せる
-
ノードがオブジェクトで管理され、nodeは自分自身からは見れるので、いったん登録して、外部から使ってしまおうという作戦
-
node.send()が次のノードに情報を伝える関数とのこと