Azure連携(4):IoT Hub受信データをAzure Functionsで処理(C#)

<< 前の記事:「Azure連携(3):IoT Hub受信データをCosmos DBへ自動保存」
>> 次の記事:「Azure連携(5):Azure FunctionsからデバイスへのMQTT送信(C#)」 … <準備中>

この記事は PLCnext Control に限らず一般的なパソコン等にも適用可能です。

前回の記事では、Azure IoT Hub で受信したデータをデータベース CosmosDB へ自動的に保存しました。

取得したデータをデータベースへ保存しておくだけでなく、クラウド上で処理を行うことが出来ると応用範囲がぐっと広がります。
ユーザが作成したプログラムを Azure のクラウド上で実行する方法には、いくつか種類があります。
(Azure Functions, Batch, Logic Apps など)

この記事では、Azure Functions サービスを使ってクラウド上で処理を行う方法を環境構築から紹介し、IoT Hub でデータが受信されるたびに処理を走らせるところまで解説します。

目次

関数作成用ローカル環境の準備

ローカルなパソコン上で Azure Functions 用の関数の作成を行うための準備を行います。

この記事では、多機能かつフリーな開発ツールである Visual Studio Code を用いる方法を紹介します。

以下のリンクの内容に沿って解説しています。
[リンク] Visual Studio Code を使用して Azure Functions を開発する | Microsoft Learn

Visual Studio Code の環境作成

Visual Studio Code のインストール

以下のサイトを参考に、お使いのパソコンへ Visual Studio Code をインストールしてください。

[リンク] Visual Studio Code – Code Editing. Redefined

拡張機能 Azure Functions for Visual Studio Code のインストール

Azure Functions for Visual Studio Code は、開発した関数をローカルなパソコン上でテスト実行したり、作成した関数を Visual Studio Code の GUI 上から Azure へアップロード (デプロイ) したりできる便利なツールです。

Azure Functions for Visual Studio Code

拡張機能 C# for Visual Studio Code のインストール

C# for Visual Studio Code は Visual Studio Code でソースコードの編集を支援してくれる便利なツールです。
必須の機能ではありませんが、今回の記事では C# によるサンプルコードを紹介するので、インストールしておくことをお勧めします。

C# for Visual Studio Code

Azure Functions Core Tools のインストール

以下のサイトを参考に、Azure Functions Core Tools をインストールしてください。

[リンク] Core Tools を使用してローカルで Azure Functions を開発する | Microsoft Learn

.NET Core SDK のインストール

今回は C# で Azure Functions を開発するため、 .NET Core SDK をインストールします。
以下のサイトから、開発用 パソコンの OS に対応するバージョンをダウンロードし、インストールしてください。

[リンク] .NET のダウンロード

なお、上記のWEBページ上で「すべての Microsoft .NET バージョン」というリンクをクリックすると、下記のように最新版以外もダウンロードすることが出来ます。

当記事のサンプルでは .NET 6.0 向けのコードを紹介しているので、.NET 6.0 をダウンロードしてインストールしてください。他のバージョンとも共存できるので、最新である .NET 8.0 等もインストールして大丈夫です。

開発環境の動作確認:簡単なHTTPトリガー関数の作成とローカル実行

簡単なHTTPトリガー関数を作成

開発環境のテスト用に、HTTP トリガーを使ったシンプルな関数を自動生成してみます。
以下の手順でコードを生成してください。

もし自動生成された sln ファイルの名前に”generated” とついていたら、ファイル名からその部分を取り払ってください。

ローカルでの動作確認

F5 キーを押すことで、作成した関数をローカル環境で試すことが出来ます。

ターミナルに表示された URL をブラウザで開いてください。

うまく動作していれば、以下のようにブラウザにメッセージが表示されます。

さらに、URL へ 「?name=あいうえお」と追加してみましょう。

C# のコードの内容通り、メッセージが変わります。

以上で、開発環境が正しく導入されたことを確認することができました。

Visual Studio Code の ターミナル上で Ctrl + x を押せば実行を中断することができます。

今回の HTTP トリガーの関数は、Azure Functions Core Tools の機能によりローカル実行することが可能ですが、内容がもう少し複雑になって Azure ストレージ (Blob, Queue, Table) の機能もローカル環境でエミュレーションしたい場合は、Azurite を別途インストールする必要があります。Azurite も Visual Studio Code の拡張機能として導入可能です。

クラウド上での関数の実行方法

次に、先ほどのサンプル関数をクラウド上へ配置 (デプロイ) し、クラウド上で実行する手順を説明します。

Azure に「関数アプリ」リソースを作成

関数をクラウド上に配置するには、まず配置先となる「関数アプリ」リソースが必要です。

Azure ポータルへログインし、下記の手順で

関数のデプロイ

まず、Visual Studio Code で Azure へサインインします。

すると、Webブラウザが自動的に開かれるので、ブラウザ上で Azure へのログイン操作を行ってください。

ブラウザを閉じたら再び Visual Studio Code で操作を続けます。

クラウド上の関数を実行

このサンプル関数は HTTP トリガなので、URL へアクセスすれば動作させることが出来ます。

クラウド上の関数へアクセスするための URL は、Azure ポータルの関数アプリのページから以下の様に取得することが出来ます。

取得したURLへパソコンの Web ブラウザからアクセスしてください。以下の様に、ローカル実行時と同様の結果を得ることができます。

ログ出力の確認

自動生成された C# のコードには、以下の様にログへ情報を出力している部分があります。

log.LogInformation("C# HTTP trigger function processed a request.");

この様にして出力されたログは、Azure ポータルの関数アプリのページで確認することができます。

以上が、Azure Functions に自作の関数を配置して実行を確認するために最低限必要となる手順です。

CosmosDB トリガー関数

開発環境が整ったので、本命である CosmosDB のレコード変更をトリガーにして実行される関数を作成しましょう。

細かくは、以下のステップが必要となるので、それぞれについて紹介していきます。
・CosmosDB トリガー関数の自動生成
・データベースへの接続文字列を格納した環境変数をクラウド上に新規作成
・CosmosDB トリガー関数内から上述の環境変数を参照

CosmosDBトリガー関数を作成

CosmosDB トリガー関数の自動生成

前述の「簡単なHTTPトリガー関数を作成」と同様の手順で、CosmosDBトリガー関数の作成を開始してください。

HTTP トリガ関数の時と表示されるメニューにいくつか相違点がありますが、基本的な手順は同じです。
異なる部分だけ抜粋して以下に紹介します。

もし Azure 上でサブスクリプションを複数作成している場合は、ここでサブスクリプションの選択を求められます。

CosmosDB アカウント名、データベース名、コンテナ名が間違っているとトリガーかかからないので注意してく ださい。大文字・小文字も区別されます。

一通りの設定が済むと、自動的にコードが生成されます。

クラウド上へ新しい環境変数を追加

CosmosDB と関数を連携させるために、クラウド上にデータベースへの接続文字列を格納した環境変数が必要なので、以下の手順で追加してください。

  • データベースへ接続するためのプライマリ接続文字列を Azure ポータル上で取得する。
  • Visual Studio Code を操作してクラウド上に新しい環境変数 CosmosDbConnectionString を作成し、値として上述のプライマリ接続文字列を与える。
  • Azure ポータル上の環境変数設定を、ローカル環境にも反映
データベースへ接続するためのプライマリ接続文字列を Azure ポータル上で取得
クラウド上へ新しい環境変数を追加

この操作はローカル PC 上の Visual Studio Code で行いますが、あくまでもクラウド環境に対する設定操作を Visual Studio Code を介して行っているだけです。
そのため、ローカル開発環境上のプロジェクトには影響を与えません。

Azure ポータル上の環境変数設定を、ローカル環境にも反映

この操作は、関数を動作させることとは関係が無いため省いても構いません。ただ今後コードを拡張したり管理したりする際に、クラウド上の環境変数についての情報がローカルにも存在した方が良いため行っておきます。

関数内から新しい環境変数を参照

ソースコード内の Connection 変数の値を、クラウド上の環境変数を参照するよう修正してください。

簡易動作確認

まずはこの段階で動作確認をしてみましょう。

デプロイ

前述した「関数のデプロイ」と同じように、CosmosDB トリガー関数をデプロイ (クラウドへ配置)してください。

ログ画面準備

動作確認のために、まずログ画面を表示しておきます。

データベースへダミーデータを書き込み

次に、ブラウザの新しいタブ、もしくは新しいウインドウで Azure ポータルの CosmosDB の画面を開いてください。

以下のように手動で新しいレコードを追加すると、CosmosDB トリガー関数がトリガーされます。

ブラウザで Azure ポータルのタブを新しく作るには、すでに開いている Azure ポータルのタブ上で右クリックして「タブを複製」を選ぶのが簡単です。

結果確認

関数がトリガーされると、先ほど開いておいたログ画面にメッセージが表示されます。

CosmosDB データの読み出し

では、前回の記事「Azure連携(3):IoT Hub受信データをCosmos DBへ自動保存」の方法でデバイスから IoT Hub へ送って CosmosDB へ記録した json データを関数内で読み出す仕組みを実装してみましょう。

・jsonライブラリをプロジェクトへ追加
・CosmosDB のデータを json

json 用ライブラリをプロジェクトへ追加

jsonデータのパースを行うために、Newtonsoft.Json ライブラリをプロジェクトへ追加します。

ライブラリの追加には Visual Studio Code の拡張機能 Nuget Package Manager を使用します。

拡張機能 Nuget Package Manager のインストール

まだ Visual Studio Code に拡張機能 Nuget Package Manager を入れていなければ、インストールしてください。

Nuget Package Manager のインストール
Newtonsoft.Json ライブラリをプロジェクトへ追加

拡張機能 Nuget Package Managerを使って、Newtonsoft.Json ライブラリをプロジェクトへ追加してください。

DBデータへアクセスするコードを追加

CosmosDB トリガー関数に、DBデータへアクセスするためのコードを追加します。

追加する場所は、下記の3箇所です。

追加箇所①:json 用ライブラリの using 宣言

using Newtonsoft.Json を追加してください。

変更前

using System;
using System.Collections.Generic;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Extensions.Logging;

変更後

using System;
using System.Collections.Generic;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;

追加箇所②:電文データ部分へアクセスするためのプロパティ追加

Azure連携(3):IoT Hub受信データをCosmos DBへ自動保存」で CosmosDB へ記録されたデータは以下の様な構造になっており、IoT Hub で受信した電文部分は “Body” という要素に格納されていることがわかっています。

この “Body” 部分へアクセスするために、プロパティを追加します。

変更前

// Customize the model with your own desired properties
    public class ToDoItem
    {
        public string id { get; set; }
        public string Description { get; set; }
    }

変更後

// Customize the model with your own desired properties
    public class ToDoItem
    {
        public string id { get; set; }
        public string Description { get; set; }
        public dynamic Body { get; set; }
    }

プロパティに “id”, “partitionkey” なども宣言すれば、コードからアクセスできるようになります。
ただし、”iothub-name” のように名前の中にプロパティ名に使えない文字を含んでいるものあります。
その場合は、「属性」の機能を用いて以下の様に2行にします。
[JsonProperty(PropertyName = “iothub-name”)]
public string iothub_name { get; set; }
こうすることで DB の iothub-name 要素に input[0].iothub_name という形でアクセス可能になります。

追加箇所③:json 文字列からの要素値の読み出し

“Body” の値を関数側で要素毎に読み出し、読み出した値をログへ出力するコードを追加します。

変更前

if (input != null && input.Count > 0)
{
    log.LogInformation("Documents modified " + input.Count);
    log.LogInformation("First document Id " + input[0].id);
}

変更後

if (input != null && input.Count > 0)
{
    log.LogInformation("Documents modified " + input.Count);
    log.LogInformation("First document Id " + input[0].id);

    // IoT Hub で受信され CosmosDB に登録された電文を json の要素毎にアクセスできるようにします。
    string json_str_Body = input[0].Body.ToString();
    dynamic data_Body = JsonConvert.DeserializeObject<dynamic>(json_str_Body);

    string device_name = data_Body.DEVICE_NAME;
    int    count       = data_Body.COUNT;
    double temp_val    = double.Parse(data_Body.TEMPERATURE.ToString());
    double temp_thr_h  = double.Parse(data_Body.TEMP_WARN_H.ToString());
    double temp_thr_l  = double.Parse(data_Body.TEMP_WARN_L.ToString());
    double hum_val     = double.Parse(data_Body.HUMIDITY.ToString()   );
    double hum_thr_h   = double.Parse(data_Body.HUM_WARN_H.ToString() );
    double hum_thr_l   = double.Parse(data_Body.HUM_WARN_L.ToString() );
    double pwr_val     = double.Parse(data_Body.POWER.ToString()      );
    double pwr_thr_h   = double.Parse(data_Body.PWR_WARN_H.ToString() );
    double pwr_thr_l   = double.Parse(data_Body.PWR_WARN_L.ToString() );
    double voltage     = double.Parse(data_Body.VOLTAGE.ToString()    );
    double current_ma  = double.Parse(data_Body.CURRENT.ToString()    );
    string timestamp   = data_Body.TIMESTAMP.ToString();

    log.LogInformation($"device_name : {device_name}");
    log.LogInformation($"count : {count}");
    log.LogInformation($"temp_val : {temp_val:F2} [Cel. deg.], temp_thr_h : {temp_thr_h:F2}, temp_thr_l : {temp_thr_l:F2}");
    log.LogInformation($"hum_val : {hum_val:F2} [%], hum_thr_h : {hum_thr_h:F2}, hum_thr_l : {hum_thr_l:F2}");
    log.LogInformation($"pwr_val : {pwr_val:F2} [W], pwr_thr_h : {pwr_thr_h:F2}, pwr_thr_l : {pwr_thr_l:F2}");
    log.LogInformation($"voltage : {voltage:F2} [V], current_ma : {current_ma:F2} [mA]");
    log.LogInformation($"timestamp : {timestamp} [JST]");
}

このコードは、

  • json_str_Body = input[0].Body.ToString(); で Body の内容を文字列として取得しています。
  • data_Body = JsonConvert.DeserializeObject<dynamic>(json_str_Body) で文字列から json の構造を持ったオブジェクトへ変換しています。
  • xxxx = data_Body.yyyy の部分は、json の要素一つ一つから値を読み出しています。
  • log.LogInformation($…… の部分は、読み出した値を読みやすい表現にして log へ出力しています。

修正後の全体イメージは以下の通りです。

最終動作確認

最終的な動作確認をしてみましょう。

デプロイ

前述した「関数のデプロイ」と同じように、CosmosDB トリガー関数をデプロイ (クラウドへ配置)してください。

ログ画面準備

前述した「ログ画面準備」とおなじように、ログ画面を表示しておいてください。

デバイスから Azure IoT Hub へデータを送信

前々回の記事「Azure連携(2):デバイスからIoT HubへのMQTT送信(Python)」で作成した Python コードを実行して下さい。

結果確認

IoT Hub へ json 形式のデータが送信されると、メッセージルーティングによってCosmosDB に新しいレコードが追加されるたびに CosmosDB トリガー関数がトリガーされ、先ほど開いておいたログ画面にメッセージが表示されます。

以上で、IoT Hub でデータが受信されるたびにクラウド上で処理を走らせることが出来るようになりました。

次回は、クラウドからエッジデバイスへに対して MQTT による送信処理を行う方法について解説します。

<< 前の記事:「Azure連携(3):IoT Hub受信データをCosmos DBへ自動保存」
>> 次の記事:「Azure連携(5):Azure FunctionsからデバイスへのMQTT送信(C#)」 … <準備中>

  • URLをコピーしました!
目次