AGL の can-low-level を読んでみる①（デコード・エンコード）

## AGL とは？
Automotive Grade Linux (以下、AGL) という コネクテッドカーに活用するオープンプラットフォームを開発する OSSのプロジェクト。
コネクテッドカーに関する 知見がつまっています。

https://jidounten-lab.com/u_agl-connected-matome

## can-low-level とは？
CAN 通信を、以下のような簡単なコマンドで解析できるようにするツール（リンクは[こちら](https://gitlab.com/automotivegradelinux/apps/agl-service-can-low-level/-/tree/master)）。

まずは subscribe で サブスクライブ。

```bash
入力 > low-can subscribe { "event": "doors.driver.open" }
出力結果 > ON-REPLY 1:low-can/subscribe: {"jtype":"afb-reply","request":{"status":"success","uuid":"a18fd375-b6fa-4c0e-a1d4-9d3955975ae8"}}
```

イベントを検知すると、以下のような結果が出てくる

```bash
出力結果 > ON-EVENT low-can/messages.doors.driver.open({"event":"low-can\/messages.doors.driver.open","data":{"name":"messages.doors.driver.open","value":true, "timestamp": 1505812906020023},"jtype":"afb-event"})
```

そのコードが[こちら](https://gitlab.com/automotivegradelinux/apps/agl-service-can-low-level/-/tree/master)にあります。
自分もまだコードを読んでいる途中なんですが、最初は分からなかったことだらけだったので、こうして記事にしてその概要をまとめようとしています。

では、そのコードを読んでみましょう。。。

の、前に OpenXC という知識が必要になるので、その説明をします。

## OpenXC

コードを読む前に、前提知識として この OSS には OpenXC という 自動車用API とも言える OSS を知っておく必要があります。

https://internet.watch.impress.co.jp/docs/news/513233.html

簡単に説明する（間違っていたらご指摘ください）と、CAN 通信ではデータフレームにデータを積み込めるんですが、そのメッセージはあくまでも bit 形式 で送られてくるので、その中に key-value でいうところの key などをつめこんでいては、容量がすぐ大量になってしまいます（CAN-FD はあるんでしょうけど）。
なので、最初にCAN 通信で送る データフレームのフォーマットを決めといておいて（何bit目にはどのデータが何bit入っている。など）、CAN 通信自体では データのみ（key-value でいうところの value のみ）を送るようにしています。

その送り方が、

http://vi-firmware.openxcplatform.com/en/v7.2.0/getting-started/getting-started.html

https://vi-firmware.openxcplatform.com/_/downloads/en/master/pdf/

に書いてあります。

![スクリーンショット 2023-03-13 0.31.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/92647/a85921e0-11f1-d82f-059a-b4d62c5875f6.png)

このような部分です。上の画像で言うと、`0x102` が CAN 通信のデータフレーム全体でいうところの 位置を示していて、その中での bit の位置・bit の長さが `bit_position`, `bit_size` に書いてあります。さらに、その bit の結果の `offset` が そこから足し引きをする数値・`factor` が掛け算をする数値に、`generic_name` が 先ほどの説明で出てきたところの key に当たる情報になっています。

OpenXC では、このようなデータのフォーマットを先に決めといて、それに従って 集めた車の情報から key を取り除いた value のみのデータフレームを作り CAN 通信で送信し、受け取った方はその メッセージを 上のフォーマットでデコードして必要な情報を取得します。

## データフレームのデコード・エンコードをコードから読んでみよう！

ここまでで、OpenXC について説明したのですが、そのデータのデコード・エンコードを読んでみましょう。
復習になりますが、データフレームのデコード・エンコードはそれぞれ、

###### エンコード ： 車の情報を集めて、その value を 上記のフォーマット情報に従って埋め込み、新しいデータフレームを作成する。
→ 車の情報を埋め込んだデータフレームの作成
###### デコード 　： エンコードされた情報と上記のフォーマット情報に従って、エンコードされたデータフレームの bit の位置情報・長さから必要な情報を取得する。
→ エンコードされた情報から必要な情報を取得

のような内容になります。

それぞれが書かれた場所は、デコードだと [/low-can-binding/can/can-decoder.cpp](https://gitlab.com/automotivegradelinux/apps/agl-service-can-low-level/-/blob/master/low-can-binding/can/can-decoder.cpp) にあり、エンコードだと [/low-can-binding/can/can-encoder.cpp](https://gitlab.com/automotivegradelinux/apps/agl-service-can-low-level/-/blob/master/low-can-binding/can/can-encoder.cpp) にあります。

では、コードを読んでみましょう！
まずは、エンコードから読んでみます。エンコードをしているコードは、`build_message` になるんですが、その中で呼び出されている `build_frame` が重要です。`build_frame` 内にある 

```c++:can-encoder.cpp
	for(const auto& sig: signal->get_message()->get_signals())
		encode_data(sig, data, false, factor, offset);
```

で、車載データを受け取り for文で回し、それを `encode_data` しています（data 部分にエンコードした情報が入っていきます）。
この encode_data の関数内の最後の部分で、

```cpp:can-encoder.cpp
	for(size_t i = new_start_byte; i <= new_end_byte ; i++)
		data[i] = data[i] | data_signal[i-new_start_byte];
```

で、該当するビットの部分にデータを挿入しています。
こうやって、車のデータをデータフレームにつめ込んでいます。

次に、デコードを見てみます。
デコードするときは、データフレームの情報のみならず、

https://gitlab.com/automotivegradelinux/apps/agl-service-can-low-level/-/blob/master/examples/agl-vcar/signals.json

![スクリーンショット 2023-03-13 0.56.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/92647/a5ea4f9c-0a47-b8ae-6e61-5080e64f821b.png)

の jsonファイルから、デコードする情報の bit位置・bit長さ・offset・factor そして、decodeの方法（ `decoder_t::decode_boolean` ）・上記の説明でいうところの key (generic_name)を取得して、必要な情報を取得します。
ここではちょうど出てきた [decode_boolean](https://gitlab.com/automotivegradelinux/apps/agl-service-can-low-level/-/blob/master/low-can-binding/can/can-decoder.cpp#L280) を見てみます。

```c++:can-decoder.cpp
openxc_DynamicField decoder_t::decode_boolean(signal_t& signal, std::shared_ptr<message_t> message, bool* send)
{
	float value = decoder_t::parse_signal_bitfield(signal, message);
	AFB_DEBUG("Decoded message from parse_signal_bitfield: %f", value);
	openxc_DynamicField decoded_value = build_DynamicField(value == 0.0 ? false : true);

	// Don't send if they is no changes
	if ((signal.get_last_value() == value && !signal.get_send_same()) || !*send )
		*send = false;

	signal.set_last_value(value);


	return decoded_value;
}
```

なんとなく、boolean を取得して、それを返しているのが分かるのではないでしょうか？
parse_signal_bitfield も内容を見れば分かるので、[マイナスの時の対応](https://gitlab.com/automotivegradelinux/apps/agl-service-can-low-level/-/blob/master/low-can-binding/can/can-decoder.cpp#L36) が少し複雑くらいなだけで、基本的には データフレーム（ `message_t` ）から bit位置とbit長さを取得して、データを返しているだけです。

ここまでで、データのエンコードとデコードを見てみました。
今回はここまでにします。
ここまで読んでいただいて、ありがとうございました。