Node-REDを使ってみる その3(ui-tableノード、emailノード)
はじめに
今回はNode-REDのui-tableノードとemailノードを使ってみました。
デスクトップPCの作業を自動化して、遠隔でも状態を確認するためにdashboard画面を確認したり、メールで通知したりするのに使おうと思っています。
ui-tableノードのデータ構造が少し分かりづらい気もしますが、ChatGPTに相談しつつ便利に使うことができました。
▼以前の記事はこちら
ui-tableノードを使ってみる
▼以下のノードを利用しました。
https://flows.nodered.org/node/node-red-node-ui-table
▼dashboardノードも一緒に利用します。
https://flows.nodered.org/node/node-red-dashboard
Node-RED Advent Calender 2024の人気ノードランキングで紹介されていたノードで、気になったので使ってみました。
▼以下の記事です。
https://qiita.com/taiponrock/items/537ccc1798c895c5e9a7
ノードのhelpに書かれていたのですが、配列に格納して使うようですね。splitノードで分割後、joinノードで配列として結合すれば、ui-tableノードで表示させることができそうです。
▼ノードの設定とhelp画面は以下のようになっています。
今回はキュー型のデータ構造でデータを管理したかったので、ChatGPTに相談しながらそのためのプログラムをfunctionノードに記述しました。
▼動作を確認するために、以下のフローを作成しました。
[{"id":"26e45d3ec99880d1","type":"function","z":"22eb2b8f4786695c","name":"function 4","func":"let queue = flow.get(\"queue\") || [];\n\nmsg.payload = queue.map((item, index) => ({ index: index + 1, value: item }));\n\nreturn msg;\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":840,"y":3700,"wires":[["ae894b4b865108e6"]]},{"id":"0d79642cd4e5e406","type":"function","z":"22eb2b8f4786695c","name":"Queue","func":"let queue = flow.get(\"queue\") || [];\nmsg.dequeued = ''\n\n// 操作の種類をチェック(msg.topicを使用)\nswitch (msg.topic) {\n case \"enqueue\": // データを追加\n queue.push(msg.payload);\n break;\n\n case \"dequeue\": // データを削除\n if (queue.length > 0) {\n let dequeuedItem = queue.shift(); // 先頭を削除\n msg.dequeued = dequeuedItem; // 削除した要素を返す\n }\n break;\n\n case \"clear\": // キューをクリア\n queue = [];\n break;\n\n default:\n node.warn(\"サポートされていない操作です: \" + msg.topic);\n}\n\n// キューを更新\nflow.set(\"queue\", queue);\n\n// 現在のキューを出力\nmsg.queue = queue;\nreturn msg;\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":690,"y":3700,"wires":[["26e45d3ec99880d1"]]},{"id":"0b519e8e17bdc5d2","type":"inject","z":"22eb2b8f4786695c","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"enqueue","payload":"","payloadType":"date","x":530,"y":3640,"wires":[["0d79642cd4e5e406"]]},{"id":"857b0f66ba530fb6","type":"inject","z":"22eb2b8f4786695c","name":"","props":[{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"dequeue","x":500,"y":3680,"wires":[["0d79642cd4e5e406"]]},{"id":"b0c2d470f6f29bf7","type":"inject","z":"22eb2b8f4786695c","name":"","props":[{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"clear","x":490,"y":3720,"wires":[["0d79642cd4e5e406"]]},{"id":"ae894b4b865108e6","type":"ui_table","z":"22eb2b8f4786695c","group":"824d75e0b288451b","name":"","order":0,"width":0,"height":0,"columns":[],"outputs":0,"cts":false,"x":990,"y":3700,"wires":[]},{"id":"824d75e0b288451b","type":"ui_group","name":"Default","tab":"e3d29ad0e240b35c","order":1,"disp":true,"width":"6","collapse":false,"className":""},{"id":"e3d29ad0e240b35c","type":"ui_tab","name":"Test","icon":"dashboard","disabled":false,"hidden":false}]
一つ目のfunctionノードでは、msg.topicがenqueue、dequeue、clearの場合で処理を分岐しています。enqueueの場合データを追加し、dequeueの場合はデータを一つ取り出します。clearの場合はデータを削除します。このデータはflow.queueに保存されます。
let queue = flow.get("queue") || [];
msg.dequeued = ''
// 操作の種類をチェック(msg.topicを使用)
switch (msg.topic) {
case "enqueue": // データを追加
queue.push(msg.payload);
break;
case "dequeue": // データを削除
if (queue.length > 0) {
let dequeuedItem = queue.shift(); // 先頭を削除
msg.dequeued = dequeuedItem; // 削除した要素を返す
}
break;
case "clear": // キューをクリア
queue = [];
break;
default:
node.warn("サポートされていない操作です: " + msg.topic);
}
// キューを更新
flow.set("queue", queue);
// 現在のキューを出力
msg.queue = queue;
return msg;
二つ目のfunctionノードでは、キューからデータを取り出しています。
let queue = flow.get("queue") || [];
msg.payload = queue.map((item, index) => ({ index: index + 1, value: item }));
return msg;
injectノードでデータの追加や取り出し、削除を試してみました。
▼表示はできたのですが、高さを調整しないと4行分しか表示されていませんでした。
▼ui-tableノードのSizeを変更しました。
▼下まで表示されるようになりました。
キュー型のデータ構造として扱うことができているのかを確認してみました。
▼先入れ先出しになっています。clearも機能しています。
キュー型のデータ構造を扱うノードとして新しく開発しておくと便利に使えそうですね。
emailノードを使ってみる
▼以下のノードを利用しました。
https://flows.nodered.org/node/node-red-node-email
▼インストールすると、以下のノードが追加されます。
今回は一番下のメールを送信するノードで、自分宛に自分のメールアドレスで送信します。
▼以前ハッカソンのときに使ったことがあります。
▼以前利用したときは以下のenebularの記事を参考にしたのですが、Googleのアプリパスワードの場所が変わっているようでした。
https://blog.enebular.com/nodes/letsuse-emailnode
Googleのアカウント画面でアプリパスワードを発行します。
▼セキュリティの欄の2段階認証プロセスで、2段階認証を有効にしておきます。
▼検索欄で「アプリパスワード」を検索しました。
▼アプリ名を入力して、作成を選択しました。
アプリパスワードが表示されるので、コピーしておきます。
▼後から表示できないのでご注意ください。
▼一度追加した後は、2段階認証プロセスの欄でアプリパスワードが表示されるようです。
先程作成したアプリパスワードをemailノードに入力します。
▼injectノードと接続しました。
▼Auth typeはBasic、送信先とUseridは自分のメールアドレス、Passwordはアプリパスワードで発行したものです。
injectノードを実行して、メールを送信してみました。
▼msg.payloadの値が送信されています。
応用例:機械学習の自動化
具体的な実装例についてここでは詳説しないのですが、最近YOLOを使った物体検出に取り組んでいます。
▼以下の記事でも利用しています。
検出したい物体のモデルが無い場合は自分でデータセットを作成して、学習させる必要があります。このとき、データセットが大きいと学習に時間がかかります。
コマンドで学習を開始させることができるのですが、CPUの使用率が90%近くなるので、一気に複数のデータセットを学習させることができません。そこで、学習させたいデータセットをキューに格納して順次に学習させ、その様子をダッシュボード画面で管理できるようにしてみました。学習終了後はメールで通知が来ます。
▼画面はこんな感じ。YOLOのモデルの選択や、データセットの結合もできます。
学習時間も把握したかったので、時間を計算するようになっています。
▼データセットはプルダウンメニューで選択できます。
▼学習させるものとして追加すると、ui-tableに追加されます。CLEARを選択するとキューに追加されたものはすべて削除されます。
これで順次に学習が行われるようになりました。あとは放置しておけば、自動的に学習が終了します。
最後に
作業を自動化するためのフローの作成は、うまくいくと楽になっていいですね。空いている時間で他の作業をしています。
YOLOの場合、学習中にデプロイすると途中でプロセスが終了してしまうので、その間はプログラムの変更を試すことができないという問題はあります。Expressでポートを変えて起動したNode-REDの環境がいくつかあると便利です。
▼ノードの検証を行う場合やソフトウェアに組み込む場合は、Expressで起動したNode-REDを利用しています。