ArduPilot with Linux その2


## 前回のおさらい
前回からだいぶ間が開きました。
前回は、機能追加を行うための基本的な事柄として、sketchの作成方法を見ました。その中で以下のことを見ました。

- setup()は最初の一回だけ呼ばれる。
- setup()が呼ばれたあとで、loop()が繰り返し呼ばれる。
- 何か、halというグローバル変数がある。少なくともコンソールへの表示は何かのインスタンス経由で行われるようだ。
- AP_HAL_MAIN()という謎マクロがソースの一番下にある。

そして、以下の疑問点を最後に列挙しました。

- setup()とloop()は誰が呼び出すのか？
- AP_HAL_MAIN()とは何者か？
- halの具体的内容は？
- 一番最初の図には「ユーザプロセス」とある。今書いたsketchとプロセスの関係は？

今回はソースを読んで、これらの疑問点を確認します。

## AP_HAL_MAIN()を確認する

AP_HAL_MAIN()は以下のヘッダファイルに定義されているマクロです。

```cpp:ardupilot/libraries/AP_HAL_Linux/AP_HAL_Linux_Main.h

#ifndef __AP_HAL_LINUX_MAIN_H__
#define __AP_HAL_LINUX_MAIN_H__

#if CONFIG_HAL_BOARD == HAL_BOARD_LINUX
#define AP_HAL_MAIN() extern "C" {\
int main (int argc, char * const argv[]) {        \
    hal.init(argc, argv);           \
        hal.scheduler->system_initialized(); \
        setup();\
        for(;;) loop();\
        return 0;\
    }\
    }
#endif // HAL_BOARD_LINUX
#endif // __AP_HAL_LINUX_MAIN_H__

```

実装を見ると、確かにsetup()が1度だけ呼ばれて、その後loop()が無限に呼ばれます。また、setup()とloop()を呼び出している関数名がmain()であることにも注目してください。

ここまでで、以下のことがわかりました。

|疑問点|対応する実装|
|:--|:--|
|setup()とloop()は誰が呼び出すのか？|AP_HAL_MAIN()で定義するmain()関数経由で呼ばれる|
|AP_HAL_MAIN()とは何者か？|C/C++で実行時に呼ばれるmain()関数を定義している|
|一番最初の図には「ユーザプロセス」とある。今書いたsketchとプロセスの関係は？|sketch実装がmain()から呼ばれている。よって、sketch自体があるプロセスの一部となっている。プロセス自体の仕組みはこれから読んでいきます。|

## halとは何者か

AP_HAL_MAIN()の中には以下の実装が含まれています。

```c:AP_HAL_MAIN()実装より抜粋
    hal.init(argc, argv);           \
        hal.scheduler->system_initialized(); \
```

halとは何者でしょうか。halというのは、[このページ](http://ardupilot.org/dev/docs/learning-ardupilot-introduction.html)のAP_HALの説明にあるとおり、ハードウェアの差異を吸収する抽象化層です。基本的には、具体的なハードの詳細を意識することなくコードの実装ができるように準備されたものです。

さて、前回読んだスケルトンコードには、以下の実装がありました。

```
const AP_HAL::HAL& hal = AP_HAL_BOARD_DRIVER;
```

halについて、詳しく知りたければ、AP_HAL_BOARD_DRIVERを見れば良さそうです。
検索すると、以下のヘッダファイルで定義されています。

```c:AP_HAL/AP_HAL_Boards.h
#elif CONFIG_HAL_BOARD == HAL_BOARD_LINUX
#define AP_HAL_BOARD_DRIVER AP_HAL_Linux
/* 以下略 */
```
AP_HAL_BOARD_DRIVERの実体はAP_HAL_Linuxだということがわかりました。
また、CONFIG_HAL_BOARDの値によって、define値が変わることもわかります。

### AP_HAL_Linuxを見る
AP_HAL_Linuxは以下のヘッダで定義されています。

```ardupilot/libraries/AP_HAL_Linux/HAL_Linux_Class.h
class HAL_Linux : public AP_HAL::HAL {
public:
    HAL_Linux();
    void init(int argc, char * const * argv) const;
};
```

また、コンストラクタは以下のファイルで実装されています。

```c:ardupilot/libraries/AP_HAL_Linux/HAL_Linux_Class.cpp
/* HAL_Linuxのコンストラクタ */
HAL_Linux::HAL_Linux() :
    AP_HAL::HAL(
        &uartADriver,
        &uartBDriver,
        &uartCDriver,
        NULL,            /* no uartD */
        &uartEDriver,
#if CONFIG_HAL_BOARD_SUBTYPE == HAL_BOARD_SUBTYPE_LINUX_BEBOP
        &i2cDriver0,
        &i2cDriver1,
        &i2cDriver2,
#else
        &i2cDriver0,
        NULL,
        NULL,
#endif
        &spiDeviceManager,
        &analogIn,
        &storageDriver,
        &uartADriver,
        &gpioDriver,
        &rcinDriver,
        &rcoutDriver,
        &schedulerInstance,
        &utilInstance)
{}

const HAL_Linux AP_HAL_Linux;
```

さて、クラス定義をみるとわかりますが、クラスHAL_Linuxは、クラスAP_HAL::HALを継承しています。
定義は、ardupilot/libraries/AP_HAL/HAL.h にあります。
AP_HAL::HALは長いのでここでは引用しません。しかし、Ardupilotでは基本となるクラスですので、**必ず目を通してください**。

|疑問点|対応する実装|
|:--|:--|
|halの具体的内容は？|Linuxベースのシステムでは、クラスHAL_LinuxのインスタンスAP_HAL_Linux。AP_HAL::HALを継承している。|

## 次回
次回は、以下の実装を掘り下げます。次回は少し難しくなるかもしれません。

```c:AP_HAL_MAIN()実装より抜粋
    hal.init(argc, argv);           \
        hal.scheduler->system_initialized(); \
```