AGL とは?
Automotive Grade Linux (以下、AGL) という コネクテッドカーに活用するオープンプラットフォームを開発する OSSのプロジェクト。
コネクテッドカーに関する 知見がつまっています。
can-low-level とは?
CAN 通信を、以下のような簡単なコマンドで解析できるようにするツール(リンクはこちら)。
まずは subscribe で サブスクライブ。
入力 > 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"}}
イベントを検知すると、以下のような結果が出てくる
出力結果 > 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"})
そのコードがこちらにあります。
自分もまだコードを読んでいる途中なんですが、最初は分からなかったことだらけだったので、こうして記事にしてその概要をまとめようとしています。
では、そのコードを読んでみましょう。。。
の、前に OpenXC という知識が必要になるので、その説明をします。
OpenXC
コードを読む前に、前提知識として この OSS には OpenXC という 自動車用API とも言える OSS を知っておく必要があります。
簡単に説明する(間違っていたらご指摘ください)と、CAN 通信ではデータフレームにデータを積み込めるんですが、そのメッセージはあくまでも bit 形式 で送られてくるので、その中に key-value でいうところの key などをつめこんでいては、容量がすぐ大量になってしまいます(CAN-FD はあるんでしょうけど)。
なので、最初にCAN 通信で送る データフレームのフォーマットを決めといておいて(何bit目にはどのデータが何bit入っている。など)、CAN 通信自体では データのみ(key-value でいうところの value のみ)を送るようにしています。
その送り方が、
に書いてあります。
このような部分です。上の画像で言うと、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 にあり、エンコードだと /low-can-binding/can/can-encoder.cpp にあります。
では、コードを読んでみましょう!
まずは、エンコードから読んでみます。エンコードをしているコードは、build_message
になるんですが、その中で呼び出されている build_frame
が重要です。build_frame
内にある
for(const auto& sig: signal->get_message()->get_signals())
encode_data(sig, data, false, factor, offset);
で、車載データを受け取り for文で回し、それを encode_data
しています(data 部分にエンコードした情報が入っていきます)。
この encode_data の関数内の最後の部分で、
for(size_t i = new_start_byte; i <= new_end_byte ; i++)
data[i] = data[i] | data_signal[i-new_start_byte];
で、該当するビットの部分にデータを挿入しています。
こうやって、車のデータをデータフレームにつめ込んでいます。
次に、デコードを見てみます。
デコードするときは、データフレームの情報のみならず、
の jsonファイルから、デコードする情報の bit位置・bit長さ・offset・factor そして、decodeの方法( decoder_t::decode_boolean
)・上記の説明でいうところの key (generic_name)を取得して、必要な情報を取得します。
ここではちょうど出てきた decode_boolean を見てみます。
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 も内容を見れば分かるので、マイナスの時の対応 が少し複雑くらいなだけで、基本的には データフレーム( message_t
)から bit位置とbit長さを取得して、データを返しているだけです。
ここまでで、データのエンコードとデコードを見てみました。
今回はここまでにします。
ここまで読んでいただいて、ありがとうございました。