PythonスクリプトをIoTデバイスにデプロイして実行する方法

 以前の記事「設定ファイルをIoTデバイスにデプロイする方法」に引き続き、9/21の enebular のアップデートで無料で使えるようになった”ファイルアセット”の使い方について説明します。

 「設定ファイルをIoTデバイスにデプロイする方法」では、設定ファイル(CSVファイル)をファイルアセットにして、enebular-agentにデプロイしました。ファイルアセットを使うと、スクリプトをenebular-agentにデプロイして実行することもできます。

 この記事では、Pythonのスクリプトを作成して、enebular-agentにデプロイして実行します。スクリプトを実行させることで、Node-REDでは難しいことも、より自由に実現できるようになります。

目次

作成するもの

 この記事で作成する機能を説明します。

 「設定ファイルをIoTデバイスにデプロイする方法」の記事では、新型コロナウィルスの感染者数をAPIから取得して、CSVファイルに記録しました。

 今回は、Pythonのスクリプトを作成して、感染者数のCSVファイルからグラフを作成します。さらに作成したグラフを、Node-REDを利用してGoogle Driveにアップロードしてみます。

準備

Python

 enebular-agentがインストールされたデバイスには、Pythonがインストールされている必要があります。今回作成するPythonのスクリプトは、Python2でもPython3でも動作しますので、どちらのバージョンがインストールされていてもかまいません。
 また、以下のパッケージをあらかじめインストールしておいてください。パッケージのバージョンは、筆者の手元のバージョンを確認しているだけですので、あまり厳密ではありません。

  • matplotlib >= 1.3.1
  • numpy >= 1.16.6

 Debianの場合、apt-getコマンドでインストールできますので、以下のようなコマンドでインストールできます(環境によってはsudoが必要かもしれません)。

apt-get install python-matplotlib
apt-get install python-numpy

Google Drive

 この記事では、作成したグラフをGoogle Driveにアップロードします。Googleアカウントを作成して、Google Driveが使える状態にしておいてください。

 Node-REDでGoogle Driveにファイルをアップロードする手順については、以下のQiitaの記事がとても分かりやすく参考になります。

「Node-RedからGoogle Driveに画像ファイルをアップロードする」 @imanao89

 この記事を参考にして、 “1. Google Developer Consoleで APIの有効化・サービスアカウント作成し、JSONファイルをダウンロード” と “2. Google Driveでフォルダを作成し、フォルダを先ほど作成したサービスアカウントに共有する” までを実行しておいてください。

実行ファイルの作成

 Pythonのスクリプトを作成します。

 以下のプログラムを、plot.pyというファイル名で作成して保存してください。

#!/usr/bin/env python

import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import numpy as np
from datetime import datetime as dt

data = np.loadtxt('/home/enebular/enebular-runtime-agent/ports/awsiot/assets/covid19.csv', delimiter=',', dtype='str')
prefs = np.loadtxt('/home/enebular/enebular-runtime-agent/ports/awsiot/assets/pref.csv', delimiter=',', dtype='str')

for pref in prefs.tolist():
    dates = []
    patients = []
    for line in data.tolist():
        if line[1] == pref:
            patients.append(line[2])
            dates.append(dt.strptime(line[0], '%Y-%m-%d'))
    fig = plt.figure()
    ax = fig.add_subplot(1, 1, 1)
    ax.plot(dates, patients)
    fig.savefig(pref + ".png")

 とても簡単なプログラムですので、簡潔に内容を説明してみます。

 data変数には、前回の記事で作成した感染者数CSVファイル(covid19.csv)を読み込みます。感染者数CSVファイルの中身は、以下のようになっています。

2021-12-24,東京都,0
2021-12-24,大阪府,0
2021-12-24,福井県,0
...

 prefs変数には、前回の記事で作成したデータ収集対象の都道府県名ファイル(prefs.csv)を読み込みます。都道府県名ファイルの中身は、以下のようになっています。

東京都,大阪府,福井県,和歌山県

 外側のforループで、都道府県毎に処理をおこないます。

 内側のforループで、dates変数に日付のデータ(CSVファイルの1列目)、patients変数に感染者数のデータ(CSVファイルの2列目)をlistデータとして格納します。

 データの格納が終わったら、dates変数を横軸、patients変数を縦軸にして折れ線グラフを作成します(ax.plot(dates, patients))。

 さらに作成したグラフをPNGファイルとして保存します(fig.savefig(pref + ".png"))。ここでは、[都道府県名].pngという形のファイル名になります (例: 東京都.png)。

ファイルのデプロイ

ファイルアセットの作成

 上述のplot.pyファイルを元にしてファイルアセットを作ります。

 enebularにログインして、プロジェクトを選択したら、マニュアルのファイルの登録にしたがってplot.pyを登録してください。ファイルアセットの名前は、自由に設定していただいて構いません。この記事では、仮にfree-fileasset-demo-pythonとします。

 次にExecution On DeployボタンをONにしてください。このボタンをONにすると、ファイルのデプロイ後にそのファイルを実行ファイルとみなして実行します。今回は他の設定項目はデフォルトのままで構いません。

 各設定項目の詳しい説明は、マニュアルのファイル設定に記載されていますので参考にしてください。

ファイルのデプロイ

 ファイルアセットfree-fileasset-demo-pythonをenebular-agentにデプロイします。前回の記事で作成したフローを実行して、感染症者数CSV(covid19.csv)が作成されていることが前提条件となります。

 ファイルデプロイの方法は、マニュアルのファイルのデプロイに詳しく記述されていますので、ここでは概要を説明します。

  1. enebularのダッシュボードから、ファイルアセットfree-fileasset-demo-pythonを選択します
  2. ファイルアセットのOverview画面には、”Deploy”ボタンがありますので押してください
  3. Connectionを選択します。Connectionの一覧から、上述の環境作成でenebular-agentをインストールした際に使用したConnectionを選びます
  4. Targetデバイスを選択します。デバイスの一覧から、上述の環境作成で指定したデバイス名を選びます

 以上の手順で、ファイルがデプロイされて実行されます。

 エラーがなければ、東京都.png大阪府.png福井県.png和歌山県.pngのようなファイルができるはずです。(ファイルができるフォルダは、/home/enebular/enebular-runtime-agent/ports/awsiot/assetsです。/home/enebular/部分はインストール時に指定するユーザーによって変化するので注意してください。)

Node-REDフローの作成

 次に作成したグラフをGoogle Driveにアップロードするために、Node-REDフローを作成します。

node-red-contrib-googleノードのインストール

 ファイルをGoogle Driveにアップロードするために、node-red-contrib-googleノードを利用します。

 enebularでフローを作成して、フローエディタを起動してください。フローは、free-fileasset-demo-driveという名称でフローを保存することにします。

 フローエディタのメニューからmanage paletteを選択します。

 Installタブに移動して、node-red-contrib-googleノードを検索し、Installボタンを押してインストールしてください。

フローのインポート

 Node-REDのフロー(JSONファイル)を添付します。フローエディタにインポートして使用してください。

[{"id":"f5e33834.322d98","type":"tab","label":"Flow 1","disabled":false,"info":""},{"id":"e7039725.da7188","type":"debug","z":"f5e33834.322d98","name":"","active":true,"tosidebar":true,"console":true,"tostatus":false,"complete":"payload","targetType":"msg","x":820,"y":100,"wires":[]},{"id":"3ed33059.2858d","type":"change","z":"f5e33834.322d98","name":"set param","rules":[{"t":"set","p":"folderid","pt":"msg","to":"16XagCyMW9wkCczhNo9H0FtWyPhN6tdhF","tot":"str"},{"t":"set","p":"filename","pt":"msg","to":"convi19.csv","tot":"str"},{"t":"set","p":"mimeType","pt":"msg","to":"text/plain","tot":"str"},{"t":"set","p":"payload","pt":"msg","to":"{\t    \"resource\": {\t        \"name\": filename,\t        \"parents\": [\t            folderid\t        ]\t    },\t    \"media\": {\t        \"mimeType\": mimeType,\t        \"body\": payload\t    },\t    \"fields\": \"id\"\t}","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":500,"y":100,"wires":[["42b4ad6a.63db74"]]},{"id":"148ae79b.9fcd18","type":"file in","z":"f5e33834.322d98","name":"covid19.csv","filename":"/home/enebular/enebular-runtime-agent/ports/awsiot/assets/covid19.csv","format":"utf8","chunk":false,"sendError":false,"encoding":"none","x":330,"y":100,"wires":[["3ed33059.2858d"]]},{"id":"44029edd.72b5c","type":"inject","z":"f5e33834.322d98","name":"kick upload","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":true,"onceDelay":"30","x":150,"y":100,"wires":[["4a3d3636.209108","148ae79b.9fcd18"]]},{"id":"4a3d3636.209108","type":"file in","z":"f5e33834.322d98","name":"Prefectures","filename":"/home/enebular/enebular-runtime-agent/ports/awsiot/assets/pref.csv","format":"utf8","chunk":false,"sendError":false,"encoding":"none","x":210,"y":180,"wires":[["87414b13.768898"]]},{"id":"87414b13.768898","type":"csv","z":"f5e33834.322d98","name":"convert to csv","sep":",","hdrin":"","hdrout":"","multi":"one","ret":"\\n","temp":"","skip":"0","strings":true,"x":400,"y":180,"wires":[["413b1f19.4cce2"]]},{"id":"413b1f19.4cce2","type":"split","z":"f5e33834.322d98","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":570,"y":180,"wires":[["60b8b9c0.cbfcb8"]]},{"id":"6bedfd5b.c3a6f4","type":"debug","z":"f5e33834.322d98","name":"","active":true,"tosidebar":true,"console":true,"tostatus":false,"complete":"payload","targetType":"msg","x":960,"y":260,"wires":[]},{"id":"4c3d0ace.8bbee4","type":"change","z":"f5e33834.322d98","name":"set param","rules":[{"t":"set","p":"folderid","pt":"msg","to":"16XagCyMW9wkCczhNo9H0FtWyPhN6tdhF","tot":"str"},{"t":"set","p":"mimeType","pt":"msg","to":"image/png","tot":"str"},{"t":"set","p":"payload","pt":"msg","to":"{\t    \"resource\": {\t        \"name\": filename,\t        \"parents\": [\t            folderid\t        ]\t    },\t    \"media\": {\t        \"mimeType\": mimeType,\t        \"body\": payload\t    },\t    \"fields\": \"id\"\t}","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":640,"y":260,"wires":[["e9d133a3.0bfaf"]]},{"id":"cc93a9d6.ba07c8","type":"file in","z":"f5e33834.322d98","name":"*.png","filename":"","format":"","chunk":false,"sendError":false,"encoding":"none","x":490,"y":260,"wires":[["4c3d0ace.8bbee4"]]},{"id":"60b8b9c0.cbfcb8","type":"template","z":"f5e33834.322d98","name":"set filename","field":"filename","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"/home/enebular/enebular-runtime-agent/ports/awsiot/assets/{{payload}}.png","output":"str","x":330,"y":260,"wires":[["cc93a9d6.ba07c8"]]},{"id":"42b4ad6a.63db74","type":"google","z":"f5e33834.322d98","name":"","google":"","api":"drive:v3","operation":"drives.create","x":650,"y":100,"wires":[["e7039725.da7188"]]},{"id":"e9d133a3.0bfaf","type":"google","z":"f5e33834.322d98","name":"","google":"","api":"drive:v3","operation":"drives.create","x":790,"y":260,"wires":[["6bedfd5b.c3a6f4"]]}]

 インポートするとエディタ上では、図のようになります。node-red-contrib-googleノード(以下、Googleノード)にエラーを示す赤い三角形がついています。これは、Google Driveにアクセスするためのクレデンシャル情報が設定されていないためです。

enebular flow image

Googleノードの設定

 では、Googleノードの設定を行います。

 Googleノードをクリックして、編集画面を開きます。API欄にはdrive:v3、Operation欄にはdrives.createが設定されているはずです。設定されていないければ、以下の図を見て設定してください。次にConnectionの設定を行います。Connectionの鉛筆アイコンをクリックしてください。

 JSON Keyの欄に、準備 Google DriveでダウンロードしたJSONファイルの内容を貼り付けます。ダウンロードしたJSONファイルをテキストエディタなどで開き、その内容をコピーして下図のように貼り付けてください。

 JSON Key欄の下のScopes欄には、以下のように設定します。これは、編集画面のAPI Scopesの欄をコピー&ペーストしただけです。本来は丁寧に必要なAPIを設定すべきですが、ここでは簡単にするために全てコピーしています。

 これで、Googleノードの設定は完了です。Googleノードは2箇所にありますので、両方に設定してください。

Google DriveのフォルダIDの設定

 ファイルをアップロードするGoogle Driveのフォルダをフローに設定します。

 まずは、アップロード先のGoogle DriveフォルダのIDを取得します。ブラウザで、対象のフォルダを開いてください。下図に示したように、ブラウザのURLのfolders/以下の部分がフォルダIDになりますので、メモをとっておいてください。

 次に、メモしたフォルダIDをフローに設定します。

 フローの図の中にset paramという名前のノードが2つあります。この2つのChangeノードが設定の対象になります(このフローでは2種類のファイルをアップロードするため、Changeノードを2つ使っています)。

 set paramノードをクリックして編集画面を開いてください。下図のようにmsg.folderidを設定している箇所があります。ここに、先程メモしたフォルダIDを設定してください。2つのノードに同じように設定します。

 これで、アップロード先のフォルダの設定が完了です。

フローの説明

 出来上がったフローは保存してください。それでは、フローの中身を確認していきます。

 フローは3段になっていますが、1段目の処理はグラフを作成する元データとなったcovid19.csvをGoogle Driveにアップロードする処理です。

フローの起動(Injectノード)

 このフローは、Injectノードで起動されます。フローのデプロイ&起動から30秒後に一回だけ起動する設定です。

covid19.csvの読み込み(file-inノード)

 Injectノードに続く1段目のFile-inノードでは、covid19.csvファイルを読み込みます(covid19.csvファイルは、「設定ファイルをIoTデバイスにデプロイする方法」で作成したcsvファイルです)。Filenameに以下のようにcovid19.csvのパスを設定すると、ファイルの内容を読み込んで、msg.payloadの内容として出力してくれます。

/home/enebular/enebular-runtime-agent/ports/awsiot/assets/covid19.csv

Google Driveアップロードの設定(changeノード)

 1段目のChangeノードでは、covid19.csvファイルをGoogle Driveにアップロードするための設定を行います。

  • msg.folderidには前述の通りGoogle DriveのフォルダIDを設定します
  • msg.filenameにはGoogle Driveにファイルを書き込む際のファイル名を設定します
  • msg.mimeTypeにはMIME形式でファイルのタイプを設定します。ここでは、text/plainと設定します
  • msg.payloadには以下の通りJSONata形式で設定します
{
    "resource": {
        "name": filename,
        "parents": [
            folderid
        ]
    },
    "media": {
        "mimeType": mimeType,
        "body": payload
    },
    "fields": "id"
}

設定ファイル(pref.csv)の読み込み(file-inノード)

 2段目と3段目のフローでは、作成されたグラフ画像を読み込んで、Google Driveにアップロードします。

 2段目のFile-inノードでは、pref.csvファイルを読み込みます。pref.csvファイルには、感染者数データを取得する都道府県名が記載されています。この都道府県名は、そのままグラフ画像のファイル名になります。

CSVの分割(csv, splitノード)

 続くCSVノードは、受け取ったファイルの内容(msg.payload)をCSVとして解釈して、これをJavaScriptのオブジェクトに変換します(下図のようなオブジェクトになります)。このCSVノードは、デフォルト設定のまま使用しています。特別な設定は行わず、ノードを配置して前後のノードと接続しています。

{
   col1: "東京都",
   col2: "大阪府",
   ...
}

 最後のSplitノードは、受け取ったJavaScriptのオブジェクトをバラバラのStringオブジェクトに分割してくれます。これで、設定ファイルの中に列挙された都道府県名を1つずつ扱うことができるようになりました(下図)。このSplitノードもCSVノードと同様に、デフォルト設定のまま使用しています。

東京都
大阪府

グラフファイルの読み込み(template, file-inノード)

 3段目のTemplateノードでは、msg.payloadにファイル名を設定します。設定内容は以下の通りです。payloadには、Splitノードで分割した都道府県名(例:東京都)が格納されています。

/home/enebular/enebular-runtime-agent/ports/awsiot/assets/{{payload}}.png

 続くFile-inノードでは、グラフ画像のファイル(例:東京都.png)を読み込みます。Outputa single Buffer objectとして出力します。Filenameは設定されていませんが、Templateノードでmsg.filenameに設定したファイルを読み込みます。

Google Driveアップロードの設定(changeノード)

 3段目のChangeノードでは、グラフ画像のファイルをGoogle Driveにアップロードするための設定を行います。設定内容は1段目のChangeノードと全く同じです。

Node-REDフローのデプロイ

 作成したNode-REDフローをデプロイします。

 enebular-agentへのフローのデプロイの方法は、マニュアルのAWS IoTへのデプロイに詳しく記述されていますので、ここでは概要を説明します。

  1. enebularのダッシュボードから、フローfree-fileasset-demo-flowを選択します
  2. フローのOverview画面には、”Deploy”ボタンがありますので押してください
  3. Connectionを選択します。Connectionの一覧から、上述の環境作成でenebular-agentをインストールした際に使用したConnectionを選びます
  4. Targetデバイスを選択します。デバイスの一覧から、上述の環境作成で指定したデバイス名を選びます

 以上の手順で、フローがデプロイされて実行されます。

 正しく動作していれば、Google Drive上のフォルダにconvid19.csvファイルとグラフ画像のファイルがアップロードされているはずです。

おわりに

 ここまで2つの記事(1回目, 2回目) で、ファイルアセットの使い方について説明しました。

 ファイルアセットは、アイディア次第でさまざまな面白い使い方ができます。この記事で、その一端でも感じていただけたなら幸いです。使用方法が分かりづらい場合は、遠慮なくenebularのサポートに連絡してみてください。