0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Node-REDでできること part6 (TIPS編)

Last updated at Posted at 2019-03-16

はじめに

やりたいこと

  • 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
      • これらの信号は、多重であったりしばらく期間が空くこともあるとする

pic1.jpg

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を文字列としていれて、コントロールしています

もっと簡単なやりかたがある気がする。。。

任意箇所からの出力

  • ログ出力などを集約しいて表示させたい
    • 仮想リンクでもよいが、サブフローから使えないのが問題
    • 仮想リンクには名前を付けられない

pic2.jpg

  • ログ出力する側のfunctionノードに名前を付け忘れました。すみません。
ログ出力する
global.set("LogSend",node.send)
ログ出力させる側
global.get("LogSend")(msg)
  • TESTから延びるノードはdebugノードにつながっていないが、ログ出力ノードから信号を出して、メッセージを出せる

  • ノードがオブジェクトで管理され、nodeは自分自身からは見れるので、いったん登録して、外部から使ってしまおうという作戦

  • node.send()が次のノードに情報を伝える関数とのこと

0
1
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
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?