can
AGL

AGL CAN binding アーキテクチャ

More than 1 year has passed since last update.

AGL CAN binding アーキテクチャ


はじめに

AGL のCANに関する個人的なメモ。

公式ドキュメントの翻訳等。


AGL CAN binding architecture

これは、CANメッセージと診断メッセージ(現在はOBD2)を記述するJSONファイルからプロジェクトに統合するためのcppファイルを生成するためのものです。

生成されたバインディングが作成されると、その結果はAGLターゲットシステム上にインストールされるウィジェットファイルになります。

binding用のCAN通信ライブラリをJSONファイルから自動生成するためのプロジェクト?

JSON -> CAN設定ジェネレータ -> 各種ベンダ性のCAN通信プログラム -> コンパイラ -> CAN低レベルBinding(共有ライブラリ)

OpenXC_to_AGL.png

AGLプロジェクトへのCAN管理の導入は単にCANメッセージのデコードや出力を可能とするだけでなく、多くのツール(Wireshark, CAN-util...)でそれを可能にします。

目標としては、共通APIとCANバスの抽象化を提供することで、システムにより高度な機能をもたらすことです。

CAN bindingは次の2つの部分から構成されることになるでしょう。

低級レイヤ、高級レイヤ、APIの三層(二層)構造?

  • 低級レイヤ:デコード、エンコード、暗号化等、トランザクション管理、キャッシング、デバッグ用途等のCANバスとのやり取り
  • 高級レイヤ:ロジック、集約(vehicle.doors.any.open というような)、高等なオペレーション

UIから共通APIを叩くと、高級レイヤはBinderイベントを発行し、低級レイヤが発行されたBinderイベントを処理する、という形式になる

CAN_level_mapping.png

  • ハイレベル:
    他のアプリケーションが接続するBinding。
    いくつかのオリジナルから提供される新しい信号や集約信号による、CANバスへの様々なアクセス手段を提供します。
    たとえば、どのドアであるか関係なく、ドアの開閉状況を示す信号です。
    また、車内に誰もいないかどうかを監視しているアプリケーションが、1mか2mほどの移動を検知した際に、予期しない動作として所有者に警報を送信する、といったことは想像に難くないでしょう。
    ハイレベルBindingは、その動作を表したイベント信号をアプリケーションに送信し、アプリケーションは電話メッセージとして、そのイベント信号を送信する、といった具合です。
  • ローレベル:
    メッセージをデコードし、アプリケーションフレームワーク経由で人間が読めるメッセージとしてサブスクライバにイベントを送信します。
    ローレベルは、いくつかのバスへの基本的なアクセス手段といくつかの基本的な数学的、統計的な機能(前回値、最小、最大、タイムスタンプ、平均化)、そして、それと同様に識別信号のみを得るための基本的なフィルタを提供します(この部分はまだ低レベルでは実装されていません)。

CAN_bindings_communication.png

最後に、低レベルバインディングは、OpenXCを使用したバイナリとしてのみ、出荷できます。OpenXCはAGL低レベルCANバインディングジェネレータ(※URLが失効しています)にインスパイアされています。

前提条件

  • AGLのバージョンはApplication frameworkバージョン0.6以上のDaring Dabの最新版である必要があります。
  • AGLジェネレータのビルド。さもなければ、カスタムローレベルCAN bindingを生成することはできません。AGLジェネレータはCAN-binder/low-can-binding/binding/ディレクトリのソース中に貼り付ける、application-generated.cppファイルを生成します。
  • SDKクイックスタートアップガイドにしたがって、AGL SDKをセットアップしてください。もしくは、AGL Developerサイトの公式ガイドを参照してください。

SDKの中にグラフィックススタックが必要な場合は、prepare_metaツールを使用してiotbzhまたはDaring Dabフレーバーを使用して環境を準備する必要があります。これを行うには、[prepare build environment] ...:のステップ4のDockerイメージで次のコマンドを実行します。

NOTE 以下のコマンドはRenesas Porterボード用プロプライエタリグラフィックドライバが/home/devel/share/proprietary-renesas-rcarディレクトリにある場合のものです。

prepare_meta -f iotbzh -o /xdt -l /home/devel/mirror -p /home/devel/share/proprietary-renesas-rcar/ -t m3ulcb -e wipeconfig -e rm_work -e cleartemp
/xdt/build/m3ulcb/agl-init-build-env

Getting started

CAN config generator 使用法

ビルド要求条件
  • CMake バージョン 3.3以降
  • g++, clang++ またはその他のC++11準拠のコンパイラ
コンパイル
source /xdt/sdk/environment-setup-aarch64-agl-linux
export PATH=$PATH:/xdt/sdk/sysroots/x86_64-aglsdk-linux/usr/bin
export WD=$(pwd)
git clone --recursive https://gerrit.automotivelinux.org/gerrit/apps/low-level-can-service -b Renesas_delivery_Q2
git clone --recursive https://gerrit.automotivelinux.org/gerrit/apps/low-level-can-generator
cd ${WD}/low-level-can-generator
mkdir -p build
cd build
cmake -G "Unix Makefiles" ..
make
命名規則

広く知られている形式である、.による命名規則を採用しました。

名前を分離・構造化した階層構造になっています。

左側の抽象度が最も高く、右に行くにつれ、具体的になります。

この規則に従った標準PID名の例を示します:

engine.load
engine.coolant.temperature
fuel.pressure
intake.manifold.pressure
engine.speed
vehicle.speed
intake.air.temperature
mass.airflow
throttle.position
running.time
EGR.error
fuel.level
barometric.pressure
commanded.throttle.position
ethanol.fuel.percentage
accelerator.pedal.position
hybrid.battery-pack.remaining.life
engine.oil.temperature
engine.torque

NOTE この命名規則に従ってCAN信号に名前を付けることをお勧めします。

サブスクリプションとアンサブスクリプションで用いるワイルドカードとして使用されているため、*のみは名前の中で使用できません。

これについては、以下の章で説明します。

利用可能なデコーダ

bindingが標準的に提供している、以下のような基本的なデコーダを使用できます。

  • decoder_t::noopDecoder:指定がなかった場合のデフォルトのデコーダ。信号のビットフィールドの生の値を返します。
  • decoder_t::booleanDecoder:数値をブール値に変換します。
  • decoder_t::stateDecoders:CAN信号の生の整数値に対応する文字列状態を見つけて、返します。
Vector CANoe データベースからのJSONの生成

注意 この章は、自動車用のツールを必要としないため、テストされていません。

gold standardCAN信号定義を保存するためにCANoeを使用する場合、JSON作成のためにOpenXC xml_to_json.py スクリプトを使用できます。

はじめに、CANoe .dbcファイルをXMLとしてエクスポートします - Vector CANdb++でこれを実行できます。

次に、上で定義したフォーマットに従って、JSONファイルを

CANoeを使用して標準CANシグナル定義を保存する場合は、OpenXC xml_to_json.pyスクリプトを使用してJSONを作成することができます。まず、Canoeの.dbcファイルをXMLとしてエクスポートします。これは、Vector CANdb ++で行うことができます。次に、以下の定義に加えて、上で定義したフォーマットに従ってJSONファイルを作成します。

  • CANメッセージ
  • メッセージ内のCAN信号名とそれらのgeneric_name
  • オプション:診断メッセージの名前とその名前

OpenXCユーティリティのインストールとxml_to_json.pyスクリプトの実行を行うには以下を実行します。

sudo pip install openxc
cd /usr/local/lib/python2.7/dist-packages/openxc/generator

Vectorからエクスポートされたデータがsignals.xmlにあり、最小限のマッピングファイルがmapping.jsonであると仮定して、スクリプトを実行した例が以下です。

python -m openxc.utils ./xml_to_json.py signals.xml mapping.json signals.json

スクリプトはmapping.jsonをスキャンし、XMLファイルから使用したいCANメッセージ、及び信号を特定します。

メッセージの必要な部分(ビット位置、ビットサイズ、オフセットなど)を取得し、結果のサブセットをJSONとして出力ファイルsignals.jsonに出力します。

結果ファイルとmapping.jsonは、コード生成スクリプトへの入力として機能します。

設定ファイルの生成

設定ファイルを生成するためには、ジェネレータの実行時に-mオプションを使用し、JSONファイルを引数として渡す必要があります。

./can-config-generator -m ../tests/basic.json -o application-generated.cpp

もし、-oオプションを省略した場合、コードは標準出力上で生成されます。

ヘッダファイルとフッタファイルを指定することもできます。

これらのファイルはそのまま挿入した場合に有効なC++の一部でなければいけません。

ヘルプを表示するには、-hオプションを使用します。

注意: 各diagnostic_messageは、bindingが使用する唯一のバスと同じバスを定義する必要があります。

サポートされたOpenXC項目

今のところ、OpenXCリファレンスのコンプライアンスは進行中であり、can-config-generatorとCAN_signalingがすぐにそれらを実装する予定です。

initializerslooperscommandshandlersノードは現在無視されています。

このジェネレータは、低レベルCAN信号bindingのOpenXCサポートステータスに従います。

注意:bindingは別の方法でバスの宣言と構成を行うため、バス項目はこのジェネレータでサポートされません。bindingのドキュメントを参照してください。

bindingのコンパイルとインストール

ビルド要求条件
  • カーネル 4.8以上
  • CMakeバージョン3.3以上
  • g++, clang++ またはその他のC++11準拠コンパイラ
コンパイル

bindingレポジトリを複製して、更新されたgitサブモジュールと生成されたファイルをコピーします。

bindingリポジトリ上で以下のコマンドを実行してください。

cd ${WD}/low-level-can-service
cp ${WD}/low-level-can-generator/build/application-generated.cpp ../low-can-binding/binding
インストール
cd ${WD}/low-level-can-service
mkdir build
cd build
cmake ..
make
make widget

手動でインストールするには、low-can-service.wgtファイルをターゲットマシンにコピーしてから、ターゲットマシン上で次のコマンドを実行します。

ホストマシン上で、ネットワーク経由でファイルをコピーします。

scp low-can-service.wgt root@<target_IP>:~

ターゲットマシン上で、wgtファイルがrootホームディレクトリ上にある場合の例が以下です。

afm-util install low-can-service.wgt
{ "added": "low-can-service@4.0" }

AGLシステムの設定

仮想CANデバイス

ターゲットマシンに接続して、仮想CANデバイスドライバをロードし、新しいVCANデバイスをセットアップするには以下のようにします。

modprobe vcan
ip link add vcan0 type vcan
ip link set vcan0 up

Linux CANデバイスに好きな名前をつけることができます。以下に、can0という名前をつける場合の例を示します。

modprobe vcan
ip link add can0 type vcan
ip link set can0 up

USB CANアダプタを用いたCANデバイス

OBD2コネクタに接続されたUSB CANアダプタを用いて車のCANバスへ実際に接続する。

接続後に、dmesgコマンドを起動し、使用するデバイスを探します。

dmesg
[...]
[  131.871441] usb 1-3: new full-speed USB device number 4 using ohci-pci
[  161.860504] can: controller area network core (rev 20120528 abi 9)
[  161.860522] NET: Registered protocol family 29
[  177.561620] usb 1-3: USB disconnect, device number 4
[  191.061423] usb 1-2: USB disconnect, device number 3
[  196.095325] usb 1-2: new full-speed USB device number 5 using ohci-pci
[  327.568882] usb 1-2: USB disconnect, device number 5
[  428.594177] CAN device driver interface
[ 1872.551543] usb 1-2: new full-speed USB device number 6 using ohci-pci
[ 1872.809302] usb_8dev 1-2:1.0 can0: firmware: 1.7, hardware: 1.0
[ 1872.809356] usbcore: registered new interface driver usb_8dev

ここではデバイスの名前はcan0です。

この例では、CANバスを500000kbpsの速度だと仮定します。

500000kbps以外にも、125000, 250000kbpsをサポートしています。

ip link set can0 type can bitrate 500000
ip link set can0 up
ip link show can0
  can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
    link/can
    can state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0
    bitrate 500000 sample-point 0.875
    tq 125 prop-seg 6 phase-seg1 7 phase-seg2 2 sjw 1
    sja1000: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
    clock 16000000

RCar Gen3ボード上では、can0はすでに組み込みデバイスとして存在しているため、CANデバイスはcan1として認識されます。

ip link set can1 type can bitrate 500000
ip link set can1 up
ip link show can1
  can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
    link/can
    can state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0
    bitrate 500000 sample-point 0.875
    tq 125 prop-seg 6 phase-seg1 7 phase-seg2 2 sjw 1
    sja1000: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
    clock 16000000

存在しているCANデバイスの名前を変更する

次のコマンドを用いてCANデバイスの名前の名前を変更し、既に存在しているcan0デバイスを他のものに移動して、他のデバイスをcan0として使用することができます。

次の例はRcar Gen3ボードでの例です。

sudo ip link set can0 down
sudo ip link set can0 name bsp-can0
sudo ip link set bsp-can0 up

これ以降、USB CANデバイスを接続するとデフォルトでcan0という名前が付けられます。

bindingの設定

bindingは起動時にシステム設定ファイル/etc/dev-mapping.confを読み込み、JSONファイルに記述された信号から論理的な名前をシステムによって初期化されたLinuxデバイス名にマッピングします。

ファイル/etc/dev-mapping.confを編集し、CANbus-mappingセクションへのマッピングを追加してください。

デフォルトのbinding設定ではhsと命名されたCANバスを使用します。なので、hsを実際のCANバスにマッピングする必要があります。以下はその例です。

  • 前章で記述したものとしての仮想CANデバイスの使用
[CANbus-mapping]
hs="vcan0"
ls="vcan1"
  • 実際のCANデバイスの使用(CANバストラフィックをcan0上のものとして仮定)
[CANbus-mapping]
hs="can0"
ls="can1"
  • Rcar Gen3ボード上には既存のcan0組み込みデバイスではなく、OBD2コネクタに接続されたUSB CANアダプタを使用したい場合(can1を使用するとします)
[CANbus-mapping]
hs="can1"

重大な警告: 設定ファイルで指定したCANバスが、CAN-config-generatorで生成されたソースファイルで指定されたものと一致していることを確認してください。

実行、テスト、使用

afm-utilツールを用いたbinding実行の古典的な方法例

afm-util run low-can-service@4.0
1

afm-utilについてはこちらを参照してください、Application Frameworkのドキュメントも同様です。

しかし、アプリケーションフレームワークが起動時に取得したセキュリティトークンが分からないため、制御や相互作用はできません。

したがって、それをテストするには、bindingを手動で起動する方がよいでしょう。次の例では、ポート1234を使用し、テスト用に空のセキュリティトークンを残します。

afb-daemon --binding=/var/lib/afm/applications/low-can-service/4.0/lib/afb-low-can.so --rootdir=/var/lib/afm/applications/low-can-service/4.0/ --port=1234 --token=1
NOTICE: binding [/usr/lib/afb/afb-dbus-binding.so] calling registering function afbBindingV1Register
NOTICE: binding /usr/lib/afb/afb-dbus-binding.so loaded with API prefix dbus
NOTICE: binding [/usr/lib/afb/authLogin.so] calling registering function afbBindingV1Register
NOTICE: binding /usr/lib/afb/authLogin.so loaded with API prefix auth
NOTICE: binding [/var/lib/afm/applications/low-can-service/4.0/libs//low-can-binding.so] calling registering function afbBindingV1Register
NOTICE: binding /var/lib/afm/applications/low-can-service/4.0/libs//low-can-binding.so loaded with API prefix low-can
NOTICE: Waiting port=1234 rootdir=/var/lib/afm/applications/low-can-service/4.0/
NOTICE: Browser URL= http:/*localhost:1234

別のターミナルで、以前にインストールしたAFB Websocket CLIツールを使用してbindingに接続します。

afb-client-demo ws://localhost:1234/api?token=1

binding APIと直接通信できるインタラクティブなセッションになります。

bindingは、この時点で2つの動詞、subscribeおよびunsubscribeを提供します。これはJSON*event*オブジェクトによって引数を取ることができます。

引数の値は、binding用のcppファイルを生成するために使用されるJSONファイルに記述されているCANメッセージgeneric_nameです。

AFB Websocket CLIツールを使用するには、次のようにします。

<api> <verb> <arguments>
  • API:low-can
  • Verb: subscribe または unsubscribe
  • Arguments: { "event": "driver.doors.open" }

サブスクリプションとアンサブスクリプション

選択したCANイベントをサブスクライブするには、CANメッセージ名をJSON引数にしてsubscribe API verbを呼び出します。

注:引数が指定されていない場合、すべてのシグナルを一度にサブスクライブします。

例:websocketセッション

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"}}

event変数中でワイルドカードを使用してサブスクリプション、アンサブスクリプションを設定できます。

全ドアイベントを受信する例:

low-can subscribe { "event" : "doors*" }
ON-REPLY 1:low-can/subscribe: {"jtype":"afb-reply","request":{"status":"success","uuid":"511c872e-d7f3-4f3b-89c2-aa9a3e9fbbdb"}}

次に、doors.driver.openという名前のイベントのCANメッセージがデコードされるたびに、イベントを受信する例です。

ON-EVENT low-can/messages.doors.driver.open({"event":"low-can\/messages.doors.driver.open","data":{"name":"messages.doors.driver.open","value":true},"jtype":"afb-event"})

イベントは、CANイベントの名前がmessages.doors.driver.openであることを示していますが、doors.driver.openに関するイベントを要求しています。

これは、すべてのCANメッセージまたは診断メッセージに、JSON親ノード名、CANメッセージ用のmessage、OBD2などの診断メッセージ用のdiagnostic_messagesのプリフィックスが付与されるためです。

これにより、一度にすべての信号をサブスクライブまたはアンサブスクライブすることができ、推奨されないようになり、そのタイプに基づいてサブスクリプション操作をよりよくフィルタリングできます。

例:

low-can subscribe { "event" : "*speed*" } --> will subscribe to all messages with speed in their name. Search will be make without prefix for it.
low-can subscribe { "event" : "speed*" } --> will subscribe to all messages begin by speed in their name. Search will be make without prefix for it.
low-can subscribe { "event" : "messages*speed*" } --> will subscribe to all CAN messages with speed in their name. Search will be on prefixed messages here.
low-can subscribe { "event" : "messages*speed" } --> will subscribe to all CAN messages ending with speed in their name. Search will be on prefixed messages here.
low-can subscribe { "event" : "diagnostic*speed*" } --> will subscribe to all diagnostic messages with speed in their name. Search will be on prefixed messages here.
low-can subscribe { "event" : "diagnostic*speed" } --> will subscribe to all diagnostic messages ending with speed in their name. Search will be on prefixed messages here.

サブスクライブと同様の方法でシグナルをアンサブスクライブすることで、イベントの受信を停止することができます

low-can unsubscribe { "event": "doors.driver.open" }
ON-REPLY 2:low-can/unsubscribe: {"jtype":"afb-reply","request":{"status":"success"}}
low-can unsubscribe { "event" : "doors*" }
ON-REPLY 3:low-can/unsubscribe: {"jtype":"afb-reply","request":{"status":"success"}}
ケイパビリティのフィルタリング

周波数を限定するのと同様に、受信したイベント通知に最小及び最大範囲を設定することができます。

利用可能なフィルターを1つ以上使用することで、引数フィルタを使用することが可能です。

  • frequency:
  • frequency:指定された信号の新しいCANイベントが通知される頻度をHertzで指定します。ブロックされた時間中にさらに変更されたCANメッセージが受信された場合、最後に有効なものがRX_CHANGEDでロックアウト後に転送されます。
  • min:サブスクライブしたクライアントにプッシュされるデコード値の下限値
  • max:サブスクライブしたクライアントにプッシュされるデコード値の上限値

順序は選択されたフィルタの数に関係なく、一度に1つ、2つ、またはすべてを使用できます。

使用例:

low-can subscribe {"event": "messages.engine.speed", "filter": { "frequency": 3, "min": 1250, "max": 3500}}
low-can subscribe {"event": "messages.engine.load", "filter": { "min": 30, "max": 100}}
low-can subscribe {"event": "messages.vehicle.speed", "filter": { "frequency": 2}}

CANアクティビティモニタリング用途にCAN utilsを使用する

AGLターゲットにプリインストールされたcan-utilsを使用することでCANのトラフィックの監視、カスタムCANメッセージの送信を行うことができます。

CANバスの監視例

candump can0

ポーターボードに接続されたUSB CAN アダプタの場合

candump can1

カスタムメッセージの送信

cansend can0 ID#DDDDAAAATTTTAAAA

以前にダンプされたCANログファイルをリプレイできます。

これらのログファイルはGitリポジトリ下のcan_samplesディレクトリ内にあります。

以下の例はAuris TOYOTA車で実際の走行に用いられたものです。

トレースはCANデバイスcan0で記録されたものなので、動作確認の際はテストシステムに正しいデバイスをマッピングすべきです。

仮想CANデバイスvcan0でのリプレイ

canplayer -I trip_test_with_obd2_vehicle_speed_requests vcan0=can0

CANデバイスcan0でのリプレイ

canplayer -I trip_test_with_obd2_vehicle_speed_requests can0

CANデバイスcan1でのリプレイ(例はポーター)

canplayer -I trip_test_with_obd2_vehicle_speed_requests can1=can0

CANサンプル

Gitリポジトリクローン

git clone https://gerrit.automotivelinux.org/gerrit/apps/low-level-can-service

サンプル

low-level-can-service/can_samplesに存在。

init_can.shはCANデバイス、init_vcan.shはVCANデバイスのロード等を行うスクリプト。

サンプルはTOYOTA Auris, Ford Focus, Citroenの3つがある。

中身

.
├── can_samples
│   ├── citroen
│   ├── ford
│   │   └── focus
│   └── toyota
│       └── auris
├── conf.d
│   ├── app-templates
│   ├── autobuild
│   │   ├── agl
│   │   └── linux
│   ├── cmake
│   └── wgt
├── docs
│   ├── _layouts
│   │   └── ebook
│   ├── images
│   └── resources
├── examples
│   ├── OBD2
│   ├── agl-vcar
│   ├── basic
│   ├── engine
│   ├── hvac
│   └── toyota
│       └── auris
├── libs
│   ├── bitfield-c
│   │   ├── src
│   │   │   ├── bitfield
│   │   │   └── canutil
│   │   └── tests
│   ├── ini-config
│   ├── isotp-c
│   │   ├── src
│   │   │   └── isotp
│   │   └── tests
│   ├── nanopb
│   │   ├── docs
│   │   │   └── logo
│   │   ├── examples
│   │   │   ├── cmake_simple
│   │   │   ├── network_server
│   │   │   ├── simple
│   │   │   ├── using_double_on_avr
│   │   │   └── using_union_messages
│   │   ├── extra
│   │   ├── generator
│   │   │   ├── nanopb
│   │   │   └── proto
│   │   │       └── google
│   │   │           └── protobuf
│   │   ├── tests
│   │   │   ├── alltypes
│   │   │   ├── alltypes_callback
│   │   │   ├── alltypes_pointer
│   │   │   ├── alltypes_proto3
│   │   │   ├── alltypes_proto3_callback
│   │   │   ├── anonymous_oneof
│   │   │   ├── backwards_compatibility
│   │   │   ├── basic_buffer
│   │   │   ├── basic_stream
│   │   │   ├── buffer_only
│   │   │   ├── callbacks
│   │   │   ├── common
│   │   │   ├── cxx_main_program
│   │   │   ├── cyclic_messages
│   │   │   ├── decode_unittests
│   │   │   ├── encode_unittests
│   │   │   ├── enum_sizes
│   │   │   ├── enum_to_string
│   │   │   ├── extensions
│   │   │   ├── extra_fields
│   │   │   ├── field_size_16
│   │   │   ├── field_size_16_proto3
│   │   │   ├── field_size_32
│   │   │   ├── fuzztest
│   │   │   │   └── sample_data
│   │   │   ├── inline
│   │   │   ├── intsizes
│   │   │   ├── io_errors
│   │   │   ├── io_errors_pointers
│   │   │   ├── mem_release
│   │   │   ├── message_sizes
│   │   │   ├── missing_fields
│   │   │   ├── multiple_files
│   │   │   │   └── subdir
│   │   │   ├── no_errmsg
│   │   │   ├── no_messages
│   │   │   ├── oneof
│   │   │   ├── options
│   │   │   ├── package_name
│   │   │   ├── regression
│   │   │   │   ├── issue_118
│   │   │   │   ├── issue_125
│   │   │   │   ├── issue_141
│   │   │   │   ├── issue_145
│   │   │   │   ├── issue_166
│   │   │   │   ├── issue_172
│   │   │   │   │   └── submessage
│   │   │   │   ├── issue_188
│   │   │   │   ├── issue_195
│   │   │   │   ├── issue_203
│   │   │   │   ├── issue_205
│   │   │   │   ├── issue_227
│   │   │   │   ├── issue_229
│   │   │   │   ├── issue_242
│   │   │   │   ├── issue_247
│   │   │   │   ├── issue_249
│   │   │   │   ├── issue_253
│   │   │   │   └── issue_256
│   │   │   ├── site_scons
│   │   │   │   └── site_tools
│   │   │   ├── special_characters
│   │   │   └── splint
│   │   └── tools
│   ├── openxc-message-format
│   │   ├── benchmark
│   │   │   └── proto
│   │   ├── gen
│   │   │   ├── cpp
│   │   │   ├── java
│   │   │   │   └── com
│   │   │   │       └── openxc
│   │   │   └── python
│   │   └── script
│   └── uds-c
│       ├── src
│       │   └── uds
│       └── tests
└── low-can-binding
    ├── binding
    ├── can
    ├── diagnostic
    └── utils