【環境構築メモ】DDSの実装の一つ「OpenSplice」の環境構築

## まずは前提：なんでOpenSpliceを調べようとしているのか？ROS2で変わる通信基盤

現在のROSの通信基盤はUDPとTCPをベースにした独自仕様の通信プロトコルとなっている。現状のROSでも、通信が安定した環境であれば、問題なく動作するが、通信状況が不安定な環境では、QoSに関わる機能がないため、動作の保証ができない。また、リアルタイム通信などの機能は、既存の独自プロトコルではサポートされていない。

そのため、ROS2のアーキテクチャデザインを決めていくなかで、通信基盤の見直しが行われ、[ZeroMQ](http://zeromq.org/)などいくつかの候補の中から、OMG標準のDDSの採用が予定されている。

DDSはOMGで仕様策定されている分散通信基盤のプロトコルで、すでにいくつかの実装が存在している。通信自体は、現在のROSの通信プロトコルと同様にPub、Subが採用されているが、QoSの確保やリアルタイム通信についても仕様に盛り込まれている。

DDSを使うようになるROS2からは、ユーザが作成するプログラムが通信を行う場合、これまでクライアントライブラリのAPI群で直接独自プロトコルを呼んでいたのが、間に一つDDSの抽象化レイヤーが入って、そのAPIを通して、いくつかある利用可能なDDSの実装を選択して使うことになる。

以下イメージをOSRFの[ROS2 Design Wiki](http://design.ros2.org/articles/ros_on_dds.html)から引用しておく。

![api_levels.png](https://qiita-image-store.s3.amazonaws.com/0/5315/1d49abaf-0e15-0299-afbb-da94bacc7cc0.png)
*ROS2 Design 「ROS on DDS」http://design.ros2.org/articles/ros_on_dds.html 　[Creative Commons Attribution 3.0](http://creativecommons.org/licenses/by/3.0/)*

採用経緯などの説明はDesign Wikiに詳細が書いてあるので、そちらを読んでもらいたい。

**現在公開されているROS2のアーリーバージョンでは、OSRFがビルドしたOpenSpliceがaptでインストールできるlibopensplice64として提供されている。そのため、ROS2のアーリーを試すだけなら、特に個別にOpenSpliceの環境を作る必要はない（はず）。** ただ、これまでの技術とことなるものが採用されるということもあって、どんなものなのか、中身を理解するのに、OpenSplice単体の環境をいったん作ってみることにした。

ちなみに、ROS2で公式にサポートされると言われているDDSの実装にはいくつか候補があって、前掲の図にもいくつかあがっている。また、OSRFがリソースの小さなマイコン上でも動作するように独自実装している[FreeRTPS](https://github.com/ros2/freertps)などもある。

FreeRTPSも中身が気になっているので、そちらは載せられるマイコンも用意して別途試してみようと考えている。

## OpenSpliceとは

OpenSpliceは、現在は[Prism Tech](http://www.prismtech.com/)社が管理しているオープンソースのDDS実装で、商用利用以外で利用可能なバージョンは[DDS Community Edition](http://www.prismtech.com/dds-community)となっている。オープンソースライセンスはLGPLが採用されている。

OMGのDDS標準では[v1.2](http://www.omg.org/spec/DDS/)のすべての仕様と、リアルタイム通信の仕様であるDDSIの[v2.1](http://www.omg.org/spec/DDSI/)の実装がされている。

各Linux、WindowsやARMアーキテクチャ上で動作するバージョンがバイナリで用意されているが、今回はソースからビルドしてみる。手元で利用しているOSがUbuntu14.04なのだが、バイナリの中でUbuntu用に用意されているものがUbuntu12向けのものしかないためだ。

バイナリ版、ソースコード版とも[ダウンロードサイト](http://www.prismtech.com/dds-community/software-downloads)からダウンロードできる。

##ソースコードからのビルド

### ツールやライブラリ群の準備
OSはUbuntuの14.04.3（64bit）で、必要なツール、ライブラリ類の手元環境のバージョンは以下の通り。

- gcc: 4.9.2(3.2.x以上が必要)
- make: 4.0(3.8以上が必要)
- awk: 4.1.1(バージョンは任意)
- flex: 2.5.39(バージョンは任意)
- bison: 3.0.2(バージョンは任意)
- perl: 5.20.2(5.8以上が必要)
- JavaSDK(※JavaのAPIやJava用のツールを利用する場合のみ): Oracle JavaSDK 1.7.0_79(1.6.0以上が必要)
- doxygen(※C#,C++のAPIドキュメントを出力する場合のみ): 1.8.9.1(1.8以上が必要)
- gSOAP:2.8.17(おそらくバージョンは任意)

最後のgSOAPはあっても無くてもよいはず。CORBAサポートのオプションは、今回は無しにしているが、SOAPとRESTについては対応できるようにgSOAPはインストールしておいた。gSOAPのインストールはaptで行える。

### 実際のビルド
実際にビルドを行う。ビルドを行う際には、設定しておく環境変数がいくつかある。具体的にはCORBAのサポートを有効にする場合のORBのパスとJAVA_HOMEになるが、今回はCORBAサポートは有効にしないので、JAVA_HOMEだけ設定しておく。JAVA_HOMEがすでに何らかの手段で（例えば.bash_profileなどに）設定済みの場合は、特に追加で行う必要はない。設定していない場合は、説明の必要もないかもだけど、以下のようにexportで設定しておく。

```shell-session:環境変数の設定
$ export JAVA_HOME=/path/hoge/java
```

続いて、ダウンロードしてきたコミュニティ版のソースコードを展開し、configureを実行する。config時にビルドのターゲットを選択する必要がある。ここでは、x86_64のreleaseを選択（ダウンロードしてきたバージョンだとメニュー15）する。configureはsourceコマンドで実行するようになっている。実行時のログは以下のような感じとなる。

```shell-session:configure実行
$ source ./configure 
Cloning into 'submods/MPC_ROOT'...
remote: Counting objects: 11833, done.
remote: Total 11833 (delta 183), reused 183 (delta 183), pack-reused 11649
Receiving objects: 100% (11833/11833), 3.77 MiB | 552.00 KiB/s, done.
Resolving deltas: 100% (10435/10435), done.
Checking connectivity... done.
Submodule path 'submods/MPC_ROOT': checked out '835cd46f730e8bebc381f0838ceea3456d6fce33'

Setup at 17:47:38 for OpenSplice - Version V6.4.151118OSS - Date 2016-01-05

Available targets are:
1 > armv6l.linux-dev
2 > armv6l.linux-release
3 > armv7l.linux-dev
4 > armv7l.linux-release
5 > x86.linux-debug
6 > x86.linux-dev
7 > x86.linux-devdat
8 > x86.linux-efence
9 > x86.linux-gcov
10 > x86.linux-release
11 > x86.linux-test
12 > x86_64.linux-debug
13 > x86_64.linux-dev
14 > x86_64.linux-gcov
15 > x86_64.linux-release
16 > x86_64.linux_icc-dev
17 > x86_64.linux_icc-release
18 > x86_64.linux_opencc-release
Please select a target number:15

GCC: OK - using version 4.9.2
NOTE: enabling link-time optimizations in release build
GLIBC: version 2.21
MAKE: OK - using GNU Make 4.0
Perl: OK - using perl version='5.20.2';
Qt: On. Using QT tools from the path (with -qt4 suffix).
GAWK: OK - using GNU Awk 4.1.1, API: 1.1 (GNU MPFR 3.1.2-p11, GNU MP 6.0.0)
BISON: OK - using bison (GNU Bison) 3.0.2
FLEX: OK - using 2.5.39
JAVAC: Warning - you should set JAVA_COMPATJAR to rt.jar for Java 1.6
OK - using JAVAC version 1.7.0_79
   JAVA_HOME is /home/tie300657/bin/java/jdk1.7
GMCS: Warning - No gmcs compiler found
   gmcs C# compiler not found, disabling SACS api build.
TAO: Warning - No TAO found
   TAO environment not set, disabling TAO related features.
JACORB: Warning - JACORB_HOME not set
   JACORB environment not set, disabling JACORB related features.
GSOAP: OK - using GSOAP version 
  setting GSOAPHOME to /usr
Doxygen: OK
Configuration OK

Variable Setup
SPLICE_TARGET = x86_64.linux-release
SPLICE_HOST = x86_64.linux-release
OSPL_HOME = /home/xxxxxxx/installs/OpenSpliceDDS6.4
SPLICE_ORB =
```
config実行前にgithubに上げられているsubmoduleのリポジトリをクローンしてくるので、プロキシ環境下などで実行する場合は注意が必要。自分も会社で実行した際は、httpsで通るように.gitのconfigを書き換えた。

後は普通にmakeとmake installを実行する。ただし、このままmakeを実行すると、Ubuntu14.04の最新の状態のGCC（4.9.x）で一部BFD関連でのリンカエラーが出る。GCCのバージョンが4.8の場合は出ないのだが、4.9以降の場合は以下のように共有ライブラリのファイルのシンボリックリンクを作成しておく必要がある。

```shell-session:リンカエラー対策
$ sudo mkdir -p /usr/lib/bfd-plugins
$ sudo ln -s /usr/lib/gcc/x86_64-linux-gnu/4.9.2/liblto_plugin.so /usr/lib/bfd-plugins/
```

これでmakeを実行すれば、普通に通るはずなのだが、gSOAPを入れた状態だと、手元の環境では、さらにサブモジュールのビルドでエラーが発生した。調べてみると、サブモジュールのビルド時にgSOAPのstdsoap2.cというソースファイルへ、一部追記している部分がある模様。aptでgSOAPをインストールすると、該当の参照先ディレクトリにはヘッダファイルしかない。他に回避の仕方があるかもしれないが、とりあえず、gSOAPのソースもaptで取得し、参照先ディレクトリにシンボリックリンクを作成して通してみた。

```shell-session:gSOAPのソースの取得とリンクの作成
$ apt-get source gsoap
$ sudo ln -s ~/path/gsoap-2.8.17/gsoap/stdsoap2.c /usr/include/stdsoap2.c
```

ここまででmakeの実行ができるようになった。いくつか警告は出るが、最後までmakeが通っていれば動作するはず。make installを実行後、以下のようにテストしてみて動けば環境は構築できているはず。

### 動作確認

SOAP/REST対応でのインストールはしてあるが、まずはスタンドアローンでhello worldのexampleが動作するか確認する。最初に実行に必要な環境変数類を設定しておく必要がある。インストールディレクトリ以下の/HDE/x86.linux/release.comがそのファイルになるが、一部インストール環境に合わせて書き換えておいてやる必要がある。書き換える場所は@@INSTALL直接編集するか、sedなどを使って書き換える。

```shell-session:release.comの書き換えと環境変数類の反映
$ sed -i "s|@@INSTALLDIR@@|$PWD|g" path/fuga/HDE/x86.linux2.6/release.com
$ source path/fuga/HDE/x86.linux2.6/release.com
```

次にhello worldのexampleをビルドしておく。今回はCとJavaを試してみる。インストールディレクトリの/HDE/x86.linux/examples/dcps/HelloWorld/以下にそれぞれの言語毎のstandaloneディレクトリがある。C、Javaそれぞれのディレクトリに移動し、makeを実行すると、publisherとsubscriberの実行ファイルができあがる。

```shell-session:Cの例
$ cd /HDE/x86.linux/examples/dcps/HelloWorld/c/standalone/
$ make
$ ll
drwxr-xr-x 3 xxxxx xxxxx  4096  1月 14 14:53 ./
drwxr-xr-x 4 xxxxx xxxxx  4096  1月 14 13:40 ../
drwxr-xr-x 2 xxxxx xxxxx  4096  1月 14 14:02 .obj/
-rw-r--r-- 1 xxxxx xxxxx   118  1月 14 14:02 HelloWorldData.h
-rw-r--r-- 1 xxxxx xxxxx   485  1月 14 14:02 HelloWorldDataDcps.h
-rw-r--r-- 1 xxxxx xxxxx 24748  1月 14 14:02 HelloWorldDataSacDcps.c
-rw-r--r-- 1 xxxxx xxxxx 17284  1月 14 14:02 HelloWorldDataSacDcps.h
-rw-r--r-- 1 xxxxx xxxxx  3160  1月 14 14:02 HelloWorldDataSplDcps.c
-rw-r--r-- 1 xxxxx xxxxx   712  1月 14 14:02 HelloWorldDataSplDcps.h
-rw-r--r-- 1 xxxxx xxxxx  1406  1月 14 13:41 Makefile
-rw-r--r-- 1 xxxxx xxxxx  2827  1月 14 13:41 Makefile.sac_helloworld_pub
-rw-r--r-- 1 xxxxx xxxxx  2832  1月 14 13:41 Makefile.sac_helloworld_sub
-rw-r--r-- 1 xxxxx xxxxx  3806  1月 14 13:41 Makefile.sac_helloworld_types
-rw-r--r-- 1 xxxxx xxxxx   291 11月 19 20:54 helloworld.mpc
-rwxr-xr-x 1 xxxxx xxxxx 27824  1月 14 14:02 libsac_helloworld_types.so*
-rw-r--r-- 1 xxxxx xxxxx   996  1月 14 14:10 ospl-error.log
-rw-r--r-- 1 xxxxx xxxxx 10331  1月 14 14:54 ospl-info.log
-rwxr-xr-x 1 xxxxx xxxxx 20840  1月 14 14:02 sac_helloworld_pub*
-rwxr-xr-x 1 xxxxx xxxxx 20832  1月 14 14:02 sac_helloworld_sub*
```

実際に実行してみる。実行には２つターミナルを用意しておき、subscriberとpublisherの両方をそれぞれ別のターミナルで実行、hellow worldのメッセージがpublisherから発行され、subscriberがそれを受け取った表示がされれば、無事動作している。

```shell-session:Cのsubscriber実行（publisher実行前）
$ ./sac_helloworld_sub
<※メッセージ待ち状態で何も表示されていない状態>
```

```shell-session:Cのpuclisher実行
$ ./sac_helloworld_sub
=== HelloWorldPublisher
=== [Publisher] writing a message containing :
    userID  : 1
    Message : "Hello World"
```

```shell-session:publisher実行後のsubscriber
$ ./sac_helloworld_sub
=== [Subscriber] Ready ...
=== [Subscriber] message received :
    userID  : 1
    Message : "Hello World"

```

Javaでも表示は同様だが、実行の仕方がそれぞれ以下のようになる。

```shell-session:Javaでのsubscriber実行
$ java -classpath $OSPL_HOME/jar/dcpssaj.jar:classes HelloWorldDataSubscriber
```

```shell-session:Javaでのpublisher実行
$ java -classpath $OSPL_HOME/jar/dcpssaj.jar:classes HelloWorldDataPublisher
```

ここまでで、一応の動作確認はできたので、実際にどんなものなのかは、おいおい動かしてみる。