スマホのセンサーとManticoreのクルマのデータを使ったSDLアプリを作ってみた

#SDLアプリコンテストって？
[SDLアプリコンテスト2019](http://sdl-contest.com)は、SDLの普及を目的としたSDLコンソーシアム日本分科会の協力で実施するもので、今回が2回目の開催となります。
本コンテストでは、四輪と二輪の両方を対象に、SDLに対応するアプリ（Android、iOSアプリ）を広く募集します。年齢、性別、国籍等は不問で、個人・チームどちらでも応募可能です。
[SDLアプリコンテスト2019](http://sdl-contest.com)の応募締切は2019年10月31日までですので、リンク先を確認して、急いで応募しよう（来年にも期待！）

##SDLとは？
[SDLアプリコンテスト2019の「SDLとは」](http://sdl-contest.com/#aboutSDL)のページを確認しよう！

#今回のサンプル「フェード現象の警告」
##フェード現象とは?
自動車やオートバイでの走行中に摩擦ブレーキを連続使用した結果、ブレーキの効き（制動力）が低下すること。
※[Wikipediaより](https://ja.wikipedia.org/wiki/フェード現象)

初心者の時、こんな感じの思い出はないでしょうか？（事故まではないかw)
<img width="863" alt="スクリーンショット 2019-10-23 19.39.31.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123800/1848b604-8556-014c-abfb-61c751f49bdb.png">

##一定の角度以上で下っていたら警告したい

以下の画像をクルマの車載モニターだとしよう。一定の角度で一定時間以上下っている場合、シフトレバー操作をエンジンブレーキがかかるポジションにしているか注意したい
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123800/e32f8b55-f93e-38c1-1934-892027e147f1.png)

##【今回未実装】警告が出ていたポイントをクラウドに収集
今回は、スマホのセンサーとManticoreのクルマのデータを使ったサンプルの説明に特化したい為、実装は省略しています。
しかし、検出ポイントをクラウドで収集して、他のドライバにシェアすることにより、

・下っている最中ではなく、付近に近づいたら事前に警告したり
・警告が発生するポイントって、案外走り屋としては面白い？

などの使い道が出てくると考えている。

##今回のソースと前提の資料
今回のサンプルは、[Github](https://github.com/oic0310/SDLFade)に格納しています。
SDLアプリとAndroidアプリの違いや基本的なサンプルについては、[SDLアプリコンテスト広島 SDLハンズオン](https://speakerdeck.com/oic0310/sdl-application-contest-in-hiroshima-69dea9af-e0e2-4650-a519-1567f6e250b6)の資料で確認していただきたい。

###サンプルの動作ムービー

リンクの[動画](https://youtu.be/fKD6Xl1AaJA)をご確認ください。

###スマホのセンサーデータを取得するには？
SDLServiceは、android.app.Serviceクラスを継承しています。なので、シンプルにSensorEventListenerをimplementsしてあげるだけです。

```java:SDLService.javaの77行目付近
public class SdlService extends Service implements SensorEventListener {
	private static final String TAG			= "SDL Vehicle";
	public static final String PORT_NAME           = "port";
```

さらに、Activityの場合は、onCreateに記載しますが、Seviceクラスの場合は、onStartCommandにSensorManagerの定義を記述します。

```java:SDLService.javaの159行目付近
	public int onStartCommand(Intent intent, int flags, int startId) {
		if (null == intent || intent.equals(null) || intent.getData() == null){Log.e("Log", "the intent in onStartCommand is null");}
		Bundle bundle = intent.getExtras();
		if (bundle != null)
		{
			TCP_PORT = bundle.getInt(PORT_NAME);
		}
		sensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
		rotationSensor = sensorManager.getDefaultSensor(Sensor.TYPE_GAME_ROTATION_VECTOR);
		sensorManager.registerListener(this, rotationSensor,
				SensorManager.SENSOR_DELAY_NORMAL);
		startProxy();

		return START_STICKY_COMPATIBILITY;
		//return START_STICKY;
	}
```

余談ですが、160行目付近のintentのNullチェックを行っている部分は、MainActivityからパラメータ渡しで情報を受け取っている部分です。
MainActivityで画面に入力されたManticoreのポート番号をSDLSeviceに渡して、Manticoreに接続するために使っています。

```java:SDLService.javaの160行目付近

		if (null == intent || intent.equals(null) || intent.getData() == null){Log.e("Log", "the intent in onStartCommand is null");}
		Bundle bundle = intent.getExtras();
		if (bundle != null)
		{
			TCP_PORT = bundle.getInt(PORT_NAME);
		}

```

センサーをの値を実際に取得しているのは、SDLService.javaの546行目付近からです。
サンプルなので超適当で、シフトレバーの状態がオートマ車はDRIVEの状態で、超雑ですが、50Hzくらいの周期でセンサーデータが飛んできているので、だいたい10秒くらい20度くらい傾いたら警告を表示するだけのサンプルになっています。


```java:SDLService.javaの546行目付近
	public final void onSensorChanged(SensorEvent event) {
		if(event.sensor.getType() ==  Sensor.TYPE_GAME_ROTATION_VECTOR){
			//シフトがドライブの時だけ
			if(prndl != null) {
				if ("DRIVE".equals(prndl.toString())) {
					float[] rotMatrix = new float[9];
					float[] rotVals = new float[3];

					SensorManager.getRotationMatrixFromVector(rotMatrix, event.values);
					SensorManager.remapCoordinateSystem(rotMatrix,
							SensorManager.AXIS_X, SensorManager.AXIS_Y, rotMatrix);

					SensorManager.getOrientation(rotMatrix, rotVals);
					float azimuth = (float) Math.toDegrees(rotVals[0]);
					float pitch = (float) Math.toDegrees(rotVals[1]);
					float roll = (float) Math.toDegrees(rotVals[2]);

					if (pitch < -20) {
						counter++;
					} else if (pitch > 20) {
						counter++;
					} else {
						counter = 0;
					}

					if (counter > 20) {
						showAlert("マニュアル車なら2～3速のギアで、オートマ車も2レンジか3レンジで速度調整してください");
						counter = 0;
					}
				}
			}
		}
	}
```

Manticoreで警告表示するWindowを出すのは、SDLService.javaの423行目付近のshowAlertメソッドを使っています。
これだけで、警告表示ができるとはすごくシンプル！alert.setDuration(5000);で５秒だけ表示するようにしています。

```java:SDLService.javaの423行目付近
	public void showAlert(String text){
		Alert alert = new Alert();
		alert.setAlertText1(text);
		alert.setDuration(5000);
		sdlManager.sendRPC(alert);
	}
```

さらに、ManticoreからText to Speechで音声を再生した場合（英語のみ）、以下のコードをコピペして"Test"の部分をしゃべらせたいワードに変更すれば音声での警告も行えます

```java:SDLService.java
sdlManager.sendRPC(new Speak(TTSChunkFactory.createSimpleTTSChunks("TEST")));
```

###Manticoreからクルマのデータ(擬似データ)を取得する
Manticoreからクルマの擬似データをSDLアプリに送信することができます。送信するための画面はManticoreの右側にあるVEHICLE DATAの部分です。
<img width="1271" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123800/5628ca69-5cc0-068b-c168-d58d8470837e.png">

今回はシフトレバーの値が取りたいので、「Prndl」の項目を選択して、シフトの状態を選択します。

<img width="1276" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123800/28115bf3-e18d-fa57-acc0-ceef17a282bc.png">

変更された都度データを受信するプログラムは、SDLService.javaの244行目付近になります。
シフトレバーの状態は、onVehicleDataNotification.getPrndl();で取得できます。
ここで書かれているNotificationListenerは、定期的にデータが送信されるわけではなく、Manticoreで値を変更した都度データが飛んでくると思ってください

```java:SDLService.javaの244行目付近
                    sdlManager.addOnRPCNotificationListener(FunctionID.ON_VEHICLE_DATA, new OnRPCNotificationListener() {
                        @Override
                        public void onNotified(RPCNotification notification) {
                            OnVehicleData onVehicleDataNotification = (OnVehicleData) notification;

                            sdlManager.getScreenManager().beginTransaction();

                            prndl = onVehicleDataNotification.getPrndl();

```

#最後に

今回開発に使った環境については、[SDLアプリコンテスト広島 ハンズオン事前資料](https://speakerdeck.com/oic0310/predocument-sdl-appcontest-in-hiroshima)に記載しています。
まだまだSDLについては、情報が少ないですが、どんどん情報公開してサンプルを充実できるようにがんばっていきます！

今回記事やハンズオンの資料を作りにあたり、[田中さんの記事](https://qiita.com/masaya3/items/ac49a630f1d7cd2eb22f)が非常に参考になりました。こちらも合わせて読んでいただければと思います。