ORiN3を使ってデバイスにアクセスしてみる - C#編

# ORiN3を使ってデバイスにアクセスしてみる

## はじめに

前回の記事でORiN3のセットアップを行い、GUIベースで簡単な動作確認を行いました。  
今回は、C#でORiN3にアクセスしてみようと思います。

この記事では以下の流れで説明していきます：

1. C#のプロジェクトの作成
1. nupkgの設定
1. サンプルプログラムを使ったデータアクセス

---

## 1. C#のプロジェクト作成

まずは、ORiN3を扱うためのC#プロジェクトを作成します。
Visual Studio 2022を使いますがVisual Studio Codeなど別のIDEでも構いません。

### プロジェクトを新規作成

コンソールアプリのプロジェクトを新規作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263117/0e4fce10-c494-404e-8c0f-5ee4f7ce36ac.png)

プロジェクト名はなんでも良いですが『ORiN3Sample』としました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263117/f6194f4c-a773-4081-a0cd-ca6cf4856c22.png)

ORiN3のアクセスに必要なnupkgは.NET Standard2.1がターゲットになっています。
対応可能なフレームワークであればどれを選択しても良いですが今回は.NET8.0を選択しました。
作成ボタンをクリックしてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263117/4b0ebc0d-f3da-46cb-b6ae-72c7d901a0ee.png)

## 2. nupkgの設定

次は必要なnupkgを設定していきます。

### NuGetパッケージマネージャーを開く

プロジェクトの依存関係をクリックしてNuGetパッケージマネージャーを起動します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263117/ac2258ff-e4f4-4761-8891-e23ff94752f6.png)

### nupkeのインストール

参照タブで **Message.Client.ORiN3** を検索して以下のnupkgをインストールします。
* Message.Client.ORiN3.Provider
* Message.Client.ORiN3.RemoteEngine

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263117/e192bd7c-426b-4f6f-bd32-a5d6f6402d3f.png)

## 3. サンプルプログラムを使ったデータアクセス

次にサンプルプログラムを作っていこうと思います。

### コードの自動生成

ORiN3の設定は [ORiN3入門：環境構築ガイド＋GUIツールで動かしてみる②](https://qiita.com/kkikzk/items/3bea0665cdfee363e5ec) で作成したプロジェクトを使います。

このような設定になっているはずです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263117/71f39f2a-7df8-4708-9f7a-9000ee701554.png)

**ORiN3 Configurator** のオブジェクトエクスプローラー上でMockプロバイダを右クリックしてソースコード生成を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263117/66f57695-0683-4a5a-9c0f-0d60d1957a4a.png)

そうするとプロバイダ生成のコードがクリップボードにコピーされますのでそれをIDEに貼り付けてみてください。

自動生成されたMainメソッドの中に貼り付けましたがいくつか調整が必要です。
usingの部分をコードの上部に移動させたのと、Mainメソッドをasyncに変更しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263117/ab983c54-cff5-48a9-97cb-a00b4b6004ef.png)

この状態でORiN3プロバイダーの起動・終了ができるはずです。

### トラブルシューティング

もしもうまくいかない場合は以下を確認ください。

#### プロキシの設定

**Message.Client.ORiN3** はgRPCで **ORiN3 Remote Engine** や **ORiN3 Provider** と通信を行います。
これはHTTPの通信になりますのでプロキシが設定されていると意図した接続先につなげられないことがあります。

#### ORiN3 Remote Engineを別PCで起動している場合

ORiN3はセキュリティの都合上、デフォルト状態ではローカルホスト以外からの接続ができないようになっています。
別PCで起動している **ORiN3 Remote Engine** に接続する場合は適切な権限設定が必要です。

### ControllerとVariableの起動

上記と同様にオブジェクトエクスプローラー上で右クリックしてコードを生成してください。
以下に生成した全コードを貼っておきます。
PulseWaveVariableにアクセスして500msごと取得した値をしコンソールに表示しています。

```C#
using Design.ORiN3.Provider.Core.V1.Telemetry;
using Message.Client.ORiN3.Provider.V1;
using Message.Client.ORiN3.RemoteEngine.V1;

namespace ORiN3Sample
{
    internal class Program
    {
        static async Task Main(string[] args)
        {
            using var cts = new CancellationTokenSource();

            // Connect to Remote Engine
            using var remoteEngineChannel = Grpc.Net.Client.GrpcChannel.ForAddress("http://127.0.0.1:7103/");
            using var remoteEngine = await RemoteEngine.AttachAsync(remoteEngineChannel, uint.MaxValue, cts.Token).ConfigureAwait(false);

            // Launching Provider
            var providerEndpoints = new ProviderEndpoint[] { new ProviderEndpoint(0, "127.0.0.1", 0, Array.Empty<byte>()) };
            var telemetryEndpoints = new TelemetryEndpoint[] { };
            var telemetryAttributes = new Dictionary<string, string> { };
            var telemetryOption = new TelemetryOption(true, telemetryEndpoints, telemetryAttributes);
            var extensions = new Dictionary<string, string> { };
            var wakeupProviderResult = await remoteEngine.WakeupProviderAsync(
                id: "643D12C8-DCFC-476C-AA15-E8CA004F48E8",
                version: "1.1.0",
                threadSafeMode: true,
                endpoints: providerEndpoints,
                logLevel: ORiN3LogLevel.Information,
                telemetryOption: telemetryOption,
                extension: extensions,
                token: cts.Token).ConfigureAwait(false);

            var providerPort = wakeupProviderResult.ProviderInformation.EndPoints
                .Where(x => Uri.TryCreate(x.Uri, UriKind.Absolute, out _))
                .Select(x => new Uri(x.Uri))
                .First(x => x.Host == "127.0.0.1")
                .Port;

            // Attach RootObject
            using var providerChannel = Grpc.Net.Client.GrpcChannel.ForAddress($"http://127.0.0.1:{providerPort}");
            using var rootObject = await ORiN3RootObject.AttachAsync(providerChannel, uint.MaxValue, cts.Token).ConfigureAwait(false);

            // Create object
            var controllerObject = await rootObject.CreateControllerAsync(
                name: "GeneralPurposeController",
                typeName: "ORiN3.Provider.ORiNConsortium.Mock.O3Object.Controller.GeneralPurposeController, ORiN3.Provider.ORiNConsortium.Mock",
                option: "{\"@Version\":\"1.1.0\"}",
                token: cts.Token).ConfigureAwait(false);

            // Connect
            await controllerObject.ConnectAsync(cts.Token).ConfigureAwait(false);

            // Create object
            var variableObject = await controllerObject.CreateVariableAsync<bool>(
                name: "PulseWaveBoolVariable",
                typeName: "ORiN3.Provider.ORiNConsortium.Mock.O3Object.Variable.PulseWaveBoolVariable, ORiN3.Provider.ORiNConsortium.Mock",
                option: "{\"@Version\":\"1.1.0\"}",
                token: cts.Token).ConfigureAwait(false);

            // Get value
            for (int i = 0; i < 10; i++)
            {
                var value = await variableObject.GetValueAsync(cts.Token).ConfigureAwait(false);
                Console.WriteLine(value);
                await Task.Delay(500).ConfigureAwait(false);
            }

            // Delete object
            await variableObject.DeleteAsync(cts.Token).ConfigureAwait(false);

            // Disconnect
            await controllerObject.DisconnectAsync(cts.Token).ConfigureAwait(false);

            // Delete object
            await controllerObject.DeleteAsync(cts.Token).ConfigureAwait(false);

            // Shutdown Provider
            await rootObject.ShutdownAsync(cts.Token).ConfigureAwait(false);
        }
    }
}
```
実行結果はこんな感じです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263117/f2cf7172-0b9f-4eea-b02a-a3af590616c0.png)


---

**関連情報（公式）**

- [ORiN協議会 GitHubリポジトリ](https://github.com/ORiN-Consortium/ORiN3)
- [デンソーウェーブ ORiN3公式ページ](https://www.denso-wave.com/ja/system/iot/product/orin3-runtime.html)
- [Microsoft Store ORiN3 Runtime](https://apps.microsoft.com/detail/xpdmcgrx6kwj25?hl=ja-JP&gl=JP)

---