【Smart Device Link】車からの情報を取得してみる

車の情報を取得する処理を説明していきたいと思います。
今回は、こちらの[Github:SDLのサンプル](https://github.com/tanaka3/SDLDisplaySampleVehicle)をベースにしています。
# 車から取れるデータ

SDLが対応している車両データは以下の通りです。
ただし、取得可能な情報は利用するSDL機器に依存します。

値は、[OnVehicleData](https://www.smartdevicelink.com/en/docs/android/master/com/smartdevicelink/proxy/rpc/OnVehicleData/)で取得可能です。

|車両データ|メソッド|型|説明|
|:--|:-:|:-:|:-------|
|GPS|getGps|[GPSData](https://www.smartdevicelink.com/en/docs/android/master/com/smartdevicelink/proxy/rpc/GPSData/)|緯度、経度、速度などのGPSデータ|
|速度|getSpeed|Double|車速(KPH)|
|RPM|getRpm|Integer|エンジンの毎分回転数|
|燃料レベル|getFuelLevel|Double|タンク内の燃料レベル（パーセント）
|燃料レベル状態|getFuelLevel_State|[ComponentVolumeStatus](https://www.smartdevicelink.com/en/docs/android/master/com/smartdevicelink/proxy/rpc/enums/ComponentVolumeStatus/)|燃料レベルの状態|
|燃料範囲|getFuelRange|[List< FuelRange >](https://www.smartdevicelink.com/en/docs/android/master/com/smartdevicelink/proxy/rpc/FuelRange/)|燃料レベルと消費量に基づいて車両が走行できるkmの推定範囲
|インスタント燃費|getInstantFuelConsumption|Double|マイクロリットルでの瞬間的な燃料消費量|
|外気温|getExternalTemperature|Double|摂氏温度での外部温度|
|VIN|getVin|String|車両識別番号|
|PRNDL|getPrndl|[PRNDL]()|選択されたギア|
|タイヤ空気圧|getTirePressure|[TireStatus]()|車両の各ホイールのタイヤ状態|
|オドメーター|getOdometer|Integer|走行距離計(Km)|
|ベルト状態|getBeltStatus|[BeltStatus](https://www.smartdevicelink.com/en/docs/android/master/com/smartdevicelink/proxy/rpc/BeltStatus/)|各シートベルトの状態|
|ボディ情報|getBodyInformation|[BodyInformation](https://www.smartdevicelink.com/en/docs/android/master/com/smartdevicelink/proxy/rpc/BodyInformation/)|各車体の状態（ドアや、点火ステータスなど）|
|デバイスステータス|getDeviceStatus|[DeviceStatus](https://www.smartdevicelink.com/en/docs/android/master/com/smartdevicelink/proxy/rpc/DeviceStatus/)|スマートフォンデバイスに関する情報|
|運転者ブレーキ|getDriverBraking|[VehicleDataEventStatus](https://www.smartdevicelink.com/en/docs/android/master/com/smartdevicelink/proxy/rpc/enums/VehicleDataEventStatus/)|ブレーキペダルの状態|
|ワイパー状態|getWiperStatus|[WiperStatus](https://www.smartdevicelink.com/en/docs/android/master/com/smartdevicelink/proxy/rpc/enums/WiperStatus/)|ワイパーの状態|
|ヘッドランプの状態|getHeadLampStatus|[HeadLampStatus](https://www.smartdevicelink.com/en/docs/android/master/com/smartdevicelink/proxy/rpc/HeadLampStatus/)|ヘッドランプの状態|
|エンジントルク|getEngineTorque|Double|ディーゼル以外のエンジンのトルク値（Nm）|
|エンジンオイルの寿命|getEngineOilLife|Float|エンジンの残存オイル寿命の推定割合|
|アクセルペダル位置|getAccPedalPosition|Double|アクセルペダルの位置（パーセント）|
|ハンドル角|getSteeringWheelAngle|Double|ステアリングホイールの現在の角度（度）
|Eコール情報|getECallInfo|[ECallInfo](https://www.smartdevicelink.com/en/docs/android/master/com/smartdevicelink/proxy/rpc/ECallInfo/)|緊急通報の状況に関する情報|
|エアバッグの状態|getAirbagStatus|[AirbagStatus](https://www.smartdevicelink.com/en/docs/android/master/com/smartdevicelink/proxy/rpc/AirbagStatus/)|車両内の各エアバッグの状態|
|緊急イベント|getEmergencyEvent|[EmergencyEvent](https://www.smartdevicelink.com/en/docs/android/master/com/smartdevicelink/proxy/rpc/EmergencyEvent/)|緊急の状況|
|クラスタモードの状態|getClusterModeStatus|[ClusterModeStatus](https://www.smartdevicelink.com/en/docs/android/master/com/smartdevicelink/proxy/rpc/ClusterModeStatus/)|電力モードがアクティブかどうか|
|My Key|getMyKey|[MyKey](https://www.smartdevicelink.com/en/docs/android/master/com/smartdevicelink/proxy/rpc/MyKey/)|緊急911などの情報|(https://www.smartdevicelink.com/en/docs/android/master/com/smartdevicelink/proxy/rpc/MyKey/)|
|ウインカー|getTurnSignal|[TurnSignal](https://www.smartdevicelink.com/en/docs/android/master/com/smartdevicelink/proxy/rpc/enums/TurnSignal/)|方向指示器の状態|
|電子パーキングブレーキの状態|getElectronicParkBrakeStatus|[ElectronicParkBrakeStatus](https://www.smartdevicelink.com/en/docs/android/master/com/smartdevicelink/proxy/rpc/enums/ElectronicParkBrakeStatus/)|パークブレーキのステータス|


[vehicle-data](https://www.smartdevicelink.com/en/guides/android/getting-vehicle-data/)

# 車から情報を取得する
車からの情報を取得する方法として、１度だけ取得する方法と、
状態が変更された時に、イベントとして取得する方法があります。
また、車両データを取得するには、HMIの状態が、`FULL `、`LIMITED `、`backgrounds`である必要があります。

##### 利用可能な情報を確認する
どのデータを公開するかは、各自動車メーカーが決定するため、
実際に使用するデータが利用可能か確認する必要があります。

`PermissionManager.isPermissionParameterAllowed`を使用すると、
使用したいデータが許可されているかを確認することができます。


```java:java
boolean allowed = sdlManager.getPermissionManager().isPermissionParameterAllowed(FunctionID.GET_VEHICLE_DATA, GetVehicleData.KEY_RPM);
```

複数の項目をまとめてチェックしたい場合は、以下のような感じで確認する事も可能です。

```java:SdlService.java
List<PermissionElement> permissionElements = new ArrayList<>();

//チェックを行う項目
List<String> keys = new ArrayList<>();
keys.add(GetVehicleData.KEY_RPM);
keys.add(GetVehicleData.KEY_SPEED);
keys.add(GetVehicleData.KEY_PRNDL);
permissionElements.add(new PermissionElement(FunctionID.GET_VEHICLE_DATA, keys));

Map<FunctionID, PermissionStatus> status = sdlManager.getPermissionManager().getStatusOfPermissions(permissionElements);

//すべてが許可されているかどうか
Log.i("Permission", "Allowed:" + status.get(FunctionID.GET_VEHICLE_DATA).getIsRPCAllowed());

//各項目ごとも可能
Log.i("Permission", "KEY_RPM　Allowed:" + status.get(FunctionID.GET_VEHICLE_DATA).getAllowedParameters().get(GetVehicleData.KEY_RPM));
```

[PermissionManager](https://www.smartdevicelink.com/en/guides/android/permission-manager/)

###### 一度だけ情報を取得する
`GetVehicleData`を`SdlManager.sendRPC`に登録することで、一度だけ情報を取得する事ができます。
取得したいデータは、`GetVehicleData.setSpeed(true)`のように指定します。

結果は、`OnRPCResponseListener`の`RPCResponse`で返却され、
指定した項目のみデータがセットされています。
_※データは呼び出し時点の情報になります_


```java:SdlService.java
GetVehicleData vdRequest = new GetVehicleData();
vdRequest.setSpeed(true);
vdRequest.setOnRPCResponseListener(new OnRPCResponseListener() {
    @Override
    public void onResponse(int correlationId, RPCResponse response) {
        if(response.getSuccess()){
            Double speed = ((GetVehicleDataResponse) response).getSpeed();
        }else{
            Log.i("SdlService", "GetVehicleData was rejected.");
        }
    }
});
sdlManager.sendRPC(vdRequest);
```
###### 定期的に情報を取得する
データが更新されたタイミングで取得する事ができます。
_※更新タイミングは１秒ごとのようです。_

**・定期取得の設定**
まず、定期受信のため`SubscribeVehicleData`を`SdlManager.sendRPC`に登録します。
こちらも、定期受信したい項目を`SubscribeVehicleData.setSpeed(true)`のように指定します。

```java:SdlService.java
//定期受信用のデータを設定する
SubscribeVehicleData subscribeRequest = new SubscribeVehicleData();
subscribeRequest.setRpm(true);         //エンジン回転数
subscribeRequest.setPrndl(true);       //シフトレーバの状態
subscribeRequest.setOnRPCResponseListener(new OnRPCResponseListener() {
    @Override
    public void onResponse(int correlationId, RPCResponse response) {
        if (response.getSuccess()) {
            Log.i("SdlService", "Successfully subscribed to vehicle data.");
        }else {
            Log.i("SdlService", "Request to subscribe to vehicle data was rejected.");
        }
    }
});
sdlManager.sendRPC(subscribeRequest);
```

**・データの取得**
設定した項目は、`OnRPCNotificationListener`で取得する事ができます。
基本的には、値が更新されたタイミングでイベントが呼び出されます。
定期受信を複数設定している場合、値の変化がない項目もあります。
変化してない項目は、`null`を返すため、必ずチェックしてください。

```java:SdlService.java
//これをすると定期的にデータが取得可能
sdlManager.addOnRPCNotificationListener(FunctionID.ON_VEHICLE_DATA, new OnRPCNotificationListener() {
    @Override
    public void onNotified(RPCNotification notification) {
        OnVehicleData onVehicleDataNotification = (OnVehicleData) notification;

        //情報に変更がない場合は、Nullが変える
        Integer rpm = onVehicleDataNotification.getRpm();
        if(rpm != null) {
        }

        PRNDL prndl = onVehicleDataNotification.getPrndl();
        if (prndl != null) {
        }
    }
});
```

**・定期取得の解除**
定期取得が不要になった場合は、必ず解除してください。
解除のため`UnsubscribeVehicleData`を`SdlManager.sendRPC`に登録します。
こちらも、解除したい項目を`SubscribeVehicleData.setSpeed(true)`のように指定します。

```java:SdlService.java
UnsubscribeVehicleData unsubscribeRequest = new UnsubscribeVehicleData();
unsubscribeRequest.setRpm(true);
unsubscribeRequest.setPrndl(true);
unsubscribeRequest.setOnRPCResponseListener(new OnRPCResponseListener() {
    @Override
    public void onResponse(int correlationId, RPCResponse response) {
	if(response.getSuccess()){
	    Log.i("SdlService", "Successfully unsubscribed to vehicle data.");
	}else{
	    Log.i("SdlService", "Request to unsubscribe to vehicle data was rejected.");
	}
    }
});
sdlManager.sendRPC(unsubscribeRequest);
```

###### 実際に動かしてみる
実際にデータの取得をやってみます。
今回は、Manticoreを使用しています。

Manticoreに接続して、右側にあるのが車両データ等を変更できる項目です。
<img width="80%" alt="" src="https://qiita-image-store.s3.amazonaws.com/0/189829/4adeea6b-054f-1b44-67b3-39843353ec2b.png">

エンジンの回転数を変更するには、*RPM*の項目を選択します。
バーを動かす事で、データのシュミレートができます。
<img width="80%" alt="" src="https://qiita-image-store.s3.amazonaws.com/0/189829/784668af-eacf-4085-930c-a8b9be9cd9be.png">

動かすと、このような感じになります。
今回は、3000回転以上だと画像を変えるように処理を入れているため、
左の画像が変化します。
<img width="80%" alt="" src="https://qiita-image-store.s3.amazonaws.com/0/189829/cdd214bf-b67a-5537-3d33-752ea4f1de21.gif">


シフトレバーを変更するには、**Prndl**の項目を選択します。
コンボボックスを選ぶ事で、設定可能な一覧が表示れます。
<img width="80%" alt="" src="https://qiita-image-store.s3.amazonaws.com/0/189829/24b35bd4-3416-cf14-a970-836baa63d0ec.png">


動かすと、このような感じになります。
Prndlを変更すると、一度だけSpeedを取得する処理を入れています。
スピードそのものを変更しても、画面のデータは更新されませんが、
Prndlを変更することで、その時点のSpeedが取得されます。
<img width="80%" alt="" src="https://qiita-image-store.s3.amazonaws.com/0/189829/e02466c3-043e-d311-3976-49fa74fd53e1.gif">


#最後に
[SDLアプリ開発キット「SDL DEVELOPMENT TOOLS」](http://ascii-store.jp/p/2018113017120/?aid=jpcampaign)は、定期的なデータ受信には対応していなそうです。
SDLは機種依存がキツくて、開発が大変な気がしてきました。。