Azure IoT Edge で Linux 向け C# カスタムモジュールを開発する

この記事の内容

  • Azure IoT Edge のカスタムモジュール開発として、Linux デバイス向けに C# でモジュールを実装します
  • Visual Studio Code と Azure IoT Edge 拡張機能を使ったモジュールプロジェクトの作成手順を紹介します
  • 温度データをフィルタリングするロジックを C# で実装し、閾値以上のメッセージのみを上流に転送する仕組みを構築します
  • モジュール名を変更したときにビルドが失敗するトラブルと、その解決策を解説します
  • コンテナレジストリへのプッシュから IoT Edge デバイスへのデプロイまでの流れを確認します

前提条件

本チュートリアルは、Azure IoT Hub と IoT Edge デバイスがすでにセットアップ済みであることを前提としています。Visual Studio Code には Azure IoT Edge 拡張機能がインストールされており、コンテナレジストリ(Azure Container Registry など)へのアクセス情報が手元にある状態で進めます。


モジュールプロジェクトの作成

Visual Studio Code を起動し、コマンドパレットから新しい IoT Edge ソリューションを作成します。

  1. コマンドパレットを開き「Azure IoT Edge: New IoT Edge Solution」を選択します
  2. ソリューション名を入力します
  3. モジュールのテンプレートとして「C# Module」を選択します
  4. モジュール名を入力します(例:SampleModule
  5. コンテナレジストリのイメージリポジトリ URL を入力します

プロジェクトが生成されると、ソリューションのフォルダ構造が作成され、modules/ 配下に C# プロジェクトが配置されます。


レジストリ資格情報の設定

生成された .env ファイルにコンテナレジストリの資格情報を設定します。

CCCOOONNNTTTAAAIIINNNEEERRR___RRREEEGGGIIISSSTTTRRRYYY___SUPESARESVRSENWRAO=MR<ED==<<>.>>azurecr.io

この情報は deployment.template.json から参照され、デバイスへのデプロイ時に使用されます。


C# モジュールのコード編集

生成された modules/<モジュール名>/Program.cs を編集して、独自のフィルタリングロジックを実装します。

温度閾値変数の追加

クラスの上部に閾値を定義する変数を追加します。

static int temperatureThreshold { get; set; } = 25;

メッセージクラスの追加

受信するテレメトリデータを扱うためのクラスを追加します。

class MessageBody
{
    public Machine machine { get; set; }
    public Ambient ambient { get; set; }
    public string timeCreated { get; set; }
}

class Machine
{
    public double temperature { get; set; }
    public double pressure { get; set; }
}

class Ambient
{
    public double temperature { get; set; }
    public int humidity { get; set; }
}

メッセージフィルタリングメソッドの追加

受信したメッセージの温度が閾値を超えている場合のみ、次のモジュールへ転送するメソッドを実装します。

static async Task<MessageResponse> FilterMessages(Message message, object userContext)
{
    var counterValue = Interlocked.Increment(ref counter);

    var moduleClient = userContext as ModuleClient;
    if (moduleClient == null)
    {
        throw new InvalidOperationException("UserContext doesn't contain expected values");
    }

    byte[] messageBytes = message.GetBytes();
    string messageString = Encoding.UTF8.GetString(messageBytes);
    Console.WriteLine($"Received message {counterValue}: [{messageString}]");

    var messageBody = JsonConvert.DeserializeObject<MessageBody>(messageString);

    if (messageBody != null && messageBody.machine.temperature > temperatureThreshold)
    {
        Console.WriteLine($"Machine temperature {messageBody.machine.temperature} " +
            $"exceeds threshold {temperatureThreshold}");

        using (var filteredMessage = new Message(messageBytes))
        {
            foreach (KeyValuePair<string, string> prop in message.Properties)
            {
                filteredMessage.Properties.Add(prop.Key, prop.Value);
            }
            filteredMessage.Properties.Add("MessageType", "Alert");
            await moduleClient.SendEventAsync("output1", filteredMessage);
        }
    }

    return MessageResponse.Completed;
}

コールバックの登録

Init メソッド内で、上記のフィルタリングメソッドをメッセージ受信時のコールバックとして登録します。

await moduleClient.SetInputMessageHandlerAsync("input1", FilterMessages, moduleClient);

deployment.template.json の編集

モジュールの配置情報を deployment.template.json に追加します。modules セクションにカスタムモジュールのエントリを追加し、routes セクションでメッセージのルーティングを定義します。

"routes": {
  "sensorToFilter": "FROM /messages/modules/SimulatedTemperatureSensor/outputs/temperatureOutput INTO BrokeredEndpoint(\"/modules/SampleModule/inputs/input1\")",
  "filterToHub": "FROM /messages/modules/SampleModule/outputs/output1 INTO $upstream"
}

ビルドとコンテナレジストリへのプッシュ

エクスプローラーで deployment.template.json を右クリックし「Build and Push IoT Edge Solution」を選択します。これにより Docker イメージがビルドされ、指定したコンテナレジストリにプッシュされます。

トラブル:モジュール名を変更するとビルドが失敗する

モジュール名を初期生成時から変更した場合、dotnet publish コマンドでプロジェクトファイルが特定できずビルドが失敗することがあります。

エラーの原因:

Dockerfile 内で実行される dotnet publish コマンドが、ソリューション内に複数のプロジェクトが存在するとみなし、どのプロジェクトをビルドするか判断できなくなります。

解決策:

modules/<モジュール名>/Dockerfile.amd64(および他のアーキテクチャ向け Dockerfile)を開き、dotnet publish の行にプロジェクトファイルを明示的に指定します。

変更前:

RUN dotnet publish -c Release -o /app

変更後:

RUN dotnet publish ./SampleModule.csproj -c Release -o /app

この修正を加えることで、ビルドが正常に完了するようになります。


IoT Edge デバイスへのデプロイ

ビルドとプッシュが完了したら、デバイスにモジュールをデプロイします。

  1. VS Code の Azure IoT Hub ペインからターゲットの IoT Edge デバイスを右クリックします
  2. 「Create Deployment for Single Device」を選択します
  3. config/ フォルダに生成された deployment.amd64.json を選択します

デプロイが完了すると、デバイス上でモジュールが起動します。


動作確認

デプロイしたモジュールが正しく動作しているか確認します。

モジュールの稼働確認

VS Code の Azure IoT Hub ペインでデバイスを展開すると、デプロイしたモジュールが表示されます。

IoT Hub に届くメッセージの確認

VS Code のターミナルから以下のコマンドでイベントをモニタリングします。

AzureIoTHub:StartMonitoringBuilt-inEventEndpoint

閾値(初期値 25 度)を超えた温度データのみがフィルタリングされて IoT Hub に送信されていることを確認できます。閾値を変更してモジュールを再デプロイすると、送信されるメッセージの条件が変わることも確認できます。


まとめ

本記事では、Azure IoT Edge で Linux 向け C# カスタムモジュールを開発するチュートリアルを解説しました。

  • Visual Studio Code と Azure IoT Edge 拡張機能を使い、C# モジュールプロジェクトを作成できます
  • Program.cs に独自のフィルタリングロジックを実装することで、閾値に基づいたメッセージ転送処理が実現できます
  • モジュール名を変更した場合は、Dockerfile 内の dotnet publish コマンドにプロジェクトファイルを明示指定することでビルドエラーを回避できます
  • コンテナレジストリへのプッシュからデバイスへのデプロイまで、VS Code 上で一貫して操作できます

IoT Edge のカスタムモジュール開発に慣れることで、エッジ側での機械学習推論や独自の前処理ロジックの実装など、より高度なエッジコンピューティングシナリオへの応用が期待できます。