モチベーション
TOPPERS/箱庭 単体ロボットシミュレータに仮想的な Bluetooth デバイスを追加することにしました.そのための実装方法として,より汎用的な技術である gRPC を利用することにしましたので,サーバー側の gRPC を利用したプログラムの作成方法について解説します.
なお,gRPC を利用しようと思った動機は,以下の2点です.
- 様々な言語でサーバー側とクライアント側を開発できる
- RPC 実装を作る必要がない(データ型定義するだけで自動生成されちゃう!)
サーバー側のプログラムは,Bluetooth 用のシリアルI/Oライブラリで,EV3RTの Bluetooth APIとの連携を可能にします.ただし,本記事では,EV3RT側の解説は含まれておりません.
※別途,gRPC クライアント側の解説記事を書く予定ですので,その際に併せて説明します~.
本記事を通して学べる gRPC の知識は?
- gRPC 概要
- gRPC のデータ定義方法
- gRPC を使ったサーバー側の設計(RAWデータ管理)
- gRPC の開発環境(C#)の作成方法
- gRPC の実装例(C#)
gRPC 概要
以下の記事がとても参考になります.
深く学びたい方は,本家サイトを参照ください.
gRPC のデータ定義方法
まず,論より証拠,今回作成した gRPC の定義は以下になります.
service SerialService {
rpc PutData (SerialPutData) returns (SerialPutResult) {}
rpc GetData (SerialGetData) returns (SerialGetResult) {}
}
service
キーワードから gRPC の定義が始まります.今回のケースでは,RPCのサービス名がSerialService
です.
そして,このサービスには,以下の2つのAPI(関数)があります(rpc
キーワードで始まる部分です).
- PutData
- GetData
それぞれの処理について簡単に説明します.
PutData
本APIは,引数に SerialPutData
というデータ型をとり,結果を SerialputResult
というデータ型で返します.クライアントがサーバーに Bluetooth 用のシリアルデータを送信する際に利用します.
※クライアントは送信したいデータを SerialPutData
に詰めます.
GetData
本APIは,引数に SerialGetData
というデータ型をとり,結果を SerialGetResult
というデータ型で返します.クライアントがサーバーから Bluetooth 用のシリアルデータを受信する際に利用します.
※クライアントは受信するデータを SerialGetResult
で受け取ります.
ここから,各データ型の定義方法について解説します.
まず,gRPC で利用するデータ型は,プロトコルバッファーで定義します.
今回作成したデータ型の定義は以下の通りです.
message SerialPutData {
int32 channel = 1;
bytes data = 2;
}
message SerialPutResult {
int32 channel = 1;
string ercd = 2;
}
message SerialGetData {
int32 channel = 1;
}
message SerialGetResult {
int32 channel = 1;
string ercd = 2;
bytes data = 3;
}
message
で始まるキーワード部分がプロトコルバッファー定義部分です.
message の中のメンバは,データ型毎にそれぞれ自由に定義できます.
-
channel
は,シリアルI/Oをマルチチャネルに対応するためのメンバです(今回は1チャネルのみしかやりませんが). -
ercd
には,サーバー側のエラーメッセージが格納されます. -
data
には,バイナリデータが格納されます.Bluetoothのシリアルデータが対応します.
たったこれだけの定義をするだけで,クライアント側とサーバー側の RPC 実装コードが様々な言語で生成されちゃうわけですから,実装方法の自由度が大きく広がりますし,実装コストも激減ですよね!!
gRPC を使ったサーバー側の設計(RAWデータ管理)
サーバー側の Bluetooth I/O 設計内容(UML)をお示しします.
なお,本設計では,UMLツールとして,astah* professionalを利用しています.直感的な操作ができるところがとても良いです(ハマります).設計検討中は常にモヤモヤしているので,ストレスなく思いを描けるツールの存在はとても大事です.さらに,設計中は様々な視点での検討が必要となるので,UML図だけでなく,DFDやロバストネス図なども使える点がさらに良いです.
##シーケンス設計
起動シーケンス
PutData シーケンス
GetData シーケンス
gRPC の開発環境(C#)の作成方法
さて,ここから実際にプログラミングの話になります.
今回のBluetoothサーバー実装言語は,C#にしました.
理由は,以下の記事にあるロガー機能がC#で実装されており,将来的に,このロガーを実機とシミュレータとで切り替えできるようにしたいという野望があるためです.
C#での gRPC の開発環境ですが,こちらの記事が大変参考になります.
gRPC の実装例(C#)
各クラスの実装は,以下で公開しております.
program.cs がユーザプログラムになります.
static void Main(string[] args)
{
string ipaddr = "172.25.0.1";
int portno = 50051;
int buffer_size = 1024 * 1024; /* 1MB */
Console.WriteLine("ipaddr=" + ipaddr + " portno=" + portno.ToString());
VirtualBluetoothSerial serial = new VirtualBluetoothSerial();
serial.SetServerInfo(ipaddr, portno, buffer_size);
serial.Open();
string data = null;
Console.WriteLine("START BT TEST>>>>");
for (int i = 1; i <= 10; i++)
{
data = "Hello World[ " + i.ToString() + " / 10 ]";
Console.WriteLine("SEND DATA: " + data);
serial.WriteLine(data);
string rcv_data = serial.ReadLine().Trim();
Console.WriteLine("RECV DATA: " + rcv_data);
if (data.Equals(rcv_data))
{
Console.WriteLine("TEST PASSED [ " + i.ToString() + " ]");
}
else
{
Console.WriteLine("TEST FAILED [ " + i.ToString() + " ]");
}
}
Console.WriteLine("<<<<END BT TEST");
serial.Close();
}
本プログラムは,シリアルデータをサーバー側からクライアントに送信して,そのデータをクライアントからそのまま送り返してもらって,送信データと受信データが一致しているかどうかをチェックしています(10回繰り返します).
デモ
このプログラムの実行するとこんな感じで動きます.
クライアント側は,EV3RTのBluetooth APIを使ってデータの送受信をするようにしています(別途こちらは解説します).