Download
latest Releases : 2.0.0-RELESE
2.0.0-RELESE | Download page |
Number of Projects
RT-Component | 153.5 |
RT-Middleware | 35 |
Tools | 22 |
Documentation | 2 |
Choreonoid
Motion editor/Dynamics simulator
OpenHRP3
Dynamics simulator
OpenRTP
Integrated Development Platform
AIST RTC collection
RT-Components collection by AIST
TORK
Tokyo Opensource Robotics Association
DAQ-Middleware
Middleware for DAQ (Data Aquisition) by KEK
データポート(基本編)では、データポートの基本的な使い方について説明しました。応用編では、もう少し踏み込んだ使い方について解説します。
データ型の利用
データポートでは、事前に定義されたデータ型 (例: TimedLong、TimedDouble 等) 以外に、自分で定義したデータ型を使用することもできます。 ただし、自分で新たなデータ型を作る前に、既に似たようなデータ型が定義されていないか確認して、その中に必要なデータ型がない場合にのみ新たなデータ型を定義することをお勧めします。
OpenRTM-aist では、以下の IDLファイルでデータポートに使用するデータ型が定義されています。
独自データ型IDLファイルの作成
データ型を定義する IDLファイルを作成します。データ型は struct キーワードで定義します。以下の基本型や、文字列型、シーケンス型が利用できます。
ここでは、MyDataType.idl に MyData というデータ型を定義することにします。 表示のためにスペースを前方に入れていますが、これは実際にIDLファイルを使用する上で不要のため削除してください。
2行目に
RTC Builderでプロジェクト作成
RTCBuilder でプロジェクトの作成を行います。
「RTC Builder Project」のアイコンをクリックし、表示したウィンドウにプロジェクト名を入力し「終了」すると、左のウィンドウに生成したプロジェクトファイルが表示します。
そこに独自データ型を配置するidlフォルダーがあるので独自データ型のIDLファイル(MyDataType.idl)を、ドラッグ&ドロップや[右クリック]>[貼り付け]などで置いてください。
プロジェクトフォルダーは、RTCBuilder起動直後に表示する「ディレクトリをワークスペースとして選択」をデフォルトで進めた場合、[ c:\Users\ユーザ名\workspase ]フォルダー以下にあります。
データポートの作成
RTCBuilder で独自データ型のidlを使ったコンポーネントの作成を行います。
データポート設定タブを開き、データポート(InPort/OutPort)を定義します。
作成したデータポートで独自データ型を使いたい場合、[Reload] をクリックするとidlフォルダーのMyDataType.idlが読み込まれ、 *データ型 のプルダウンで新たに定義した MyData を選択できます。
その他、コンポーネント作成に必要な項目の設定が終わったら、基本タブに戻り、[コード生成] ボタンをクリックし、コードの生成を行います。
データポート、コネクタのコールバックの利用
InPort は isNew() でデータの到着の有無を確認して、read() で読みだす、あるいは OutPort は write() でデータを送り出す、ということについてはすでに述べました。
例えば、InPort はデータが来てから、onExecute() 等などの関数内で、isNew() を呼び read() を呼び出すまでデータを取得することはできません。 onExecute() の周期が非常に速かったとしても、データが InPort に到着するタイミングと、実際に処理が行われるタイミングは非同期に行われます。
データが到着してすぐに、すなわち同期的に処理を行いたい場合にはどうすればよいのでしょうか。これを実現する方法として、OpenRTM-aist ではデータポートやコネクタの種々の処理のタイミングで呼び出されるコールバックを定義しています。
コールバックには大きく分けて、1) InPort、2) OutPort、3) コネクタ、4) ポート の4種類のコールバックが用意されています。
InPort のコールバック
InPort には、以下の2種類のコールバックが用意されています。 これらは rtm/PortCallback.h において定義されています。
OnRead コールバックは read() が呼び出されたときに、OnReadConvert は read() が呼び出されたとき、呼び出し元にある種の変換を施したデータを返すために使用するコールバックです。
それぞれのコールバックは、rtm/PortCallback.h で定義されているそれぞれのファンクタの基底クラスを継承することにより実装します。
以下にそれぞれの実装例を示します。
OnRead を継承した MyOnRead ファンクタでは、コンストラクタで出力ストリーム std::ostream を渡しています。どこかでオープンしたファイル出力ストリーム std::ofstream 等を渡すことを意図しています。 ファンクタの実体である operator()() では、出力ストリームと標準出力に対して、文字列を出力しています。このように、ファンクタでは、予めコンストラクタなどで状態変数を渡すことで、他のオブジェクトに対する呼び出しも実現することができます。
一方 OnReadConvert<T> を継承した MyOnReadConvert は operator()(constT&) のみを実装しています。この関数の引数には、read() が呼ばれたときにInPort 変数に読みだされる前のデータが渡されます。 この関数内で何らかの処理を行い return で返したデータは InPort 変数に書き込まれます。この例では、データ型に data というメンバがあり、かつ乗算演算子が定義されているという前提で自乗を計算して返しています。 適切なメンバがない変数型を使用すればコンパイルエラーになります。
さて、このファンクタを実際にコンポーネントに組み込んでみましょう。InPort を使用しているサンプルとして、ここでは OpenRTM-aist に含まれているサンプルである ConsoleOut を利用します。ConsoleOut は OpenRTM-aist のソースを展開すると、
の下に、また Linux 等でパッケージ等からインストールすると、
の下にソースコードがあります。
まず、上記のクラス定義を、ConsoleOut.h に記述します。クラス定義は、本来別のソースに記述した方が良いのですが、ファンクタクラスは、このコンポーネント内でしか使用せず、内容も短いものですので、こういう場合はヘッダ内で実装も含めて定義しても構わないでしょう。
まず、ConsoleOut クラスの宣言の前に、コールバックファンクタ MyOnRead とMyOnReadConvert を宣言します。 これらのクラスのポインタ変数をメンバとして持たせるために、private の部分に、それぞれのポインタ変数を宣言します。 このとき、MyOnRead/MyOnReadConvert ともに、クラステンプレートの型引数にこのコンポーネントの InPort の型と同じ、TimedLong を与えていることに注意してください。
OutPort のコールバック
OutPort には、以下の2種類のコールバックが用意されています。 これらは rtm/PortCallback.h において定義されています。
OnWrite コールバックは write() が呼び出された際に、OnWriteConvert はwrite() が呼び出された際に、ある種の変換を施したデータを送信するために使用するコールバックです。
それぞれのコールバックは、InPort と同様 rtm/PortCallback.h で定義されているそれぞれのファンクタの基底クラスを継承することにより実装します。
以下にそれぞれの実装例を示します。
コールバック用のファンクタの書き方は、InPort の OnRead/OnReadConvert とほぼ同じです。OnWrite を継承した MyOnWrite ファンクタでは、コンストラクタで出力ストリーム std::ostream を渡しています。 どこかでオープンしたファイル出力ストリーム std::ofstream 等を渡すことを意図しています。ファンクタの実体である operator() では、出力ストリームと標準出力に対して、文字列を出力しています。 このように、ファンクタでは、予めコンストラクタなどで状態変数を渡すことで、他のオブジェクトに対する呼び出しも実現することができます。
一方 OnReadConvert<T> を継承した MyOnReadConvert は operator()(constT&) のみを実装しています。この関数の引数には、read() を呼んだときに InPort 変数に読みだされる前のデータが渡されます。この関数内で何らかの処理を行い return で返したデータは InPort 変数に書き込まれます。 この例では、データ型に data というメンバがあり、かつ乗算演算子が定義されているという前提で自乗を計算して返しています。適切なメンバがない変数型を使用すればコンパイルエラーになります。
コネクタ・バッファのコールバック
コネクタ
コネクタはバッファおよび通信路を抽象化したオブジェクトです。図に示すように、OutPort と InPort の間に存在し、OutPort からは write() 関数によりデータの書き込み、InPort からは read() 関数によりデータの読み出しが行われます。 コネクタは、データがどのような手段で OutPort から InPort へ伝送されるかを抽象化し隠蔽します。
OutPort はコネクタ内のバッファに対して、OutPort は複数の InPort へ接続することができますが、一つの接続につき、一つのコネクタが生成されます。(実際には InPort も複数の接続を同時に持つこともできますが、データを区別する方法がないので、通常は用いません。) つまり、接続が3つあれば、コネクタが3つ存在し、それぞれに対して書き込みのステータスが存在することになります。
また、これらの機能のために、OutPort/InPort 一対に対して、それぞれ一つコネクタが存在する必要があることがわかります。 さらに、コネクタをサブスクリプション型に対応した実装レベルでモデル化するにあたり、パブリッシャと呼ばれる非同期通信のためのオブジェクトを導入しました。
データポートは接続が確立されると、1つの接続につき1つのコネクタオブジェクトを生成します。コネクタは、OutPort と InPort をつなぐデータストリームの抽象チャネルです。
ポートのコールバック
ステータス
データポートは、データの送受信を行った際に、ステータスを返します。 ステータスは、rtm/DataPortStatus.h で定義されています。
PORT_OK, PORT_ERROR, SEND_FULL, SEND_TIMEOUT, CONNECTION_LOST, UNKNOWN_ERROR
PORT_OK, PORT_ERROR, BUFFER_ERROR, BUFFER_FULL, BUFFER_TIMEOUT, UNKNOWN_ERROR
PORT_OK, PORT_ERROR, RECV_EMPTY, RECV_TIMEOUT, CONNETION_LOST, UNKNOWN_ERROR
独自データポートインターフェースの作成
独自データポートインターフェースの作成例を示します。
以下の関数、クラスの定義が必要です。
プロバイダクラス (InPortTestProvider)
データポートインターフェース(Push型)のプロバイダクラスです。 コンシューマ側で put関数を呼び出した際に、何らかの方法によりプロバイダ側にデータを転送する必要があります。 このサンプルではコンシューマ側の put関数呼び出し時にファイルにデータを書き込み、プロバイダ側でファイルからデータを読み込むことでデータの転送を行っています。
データポートインターフェース(Push型)のコンシューマクラスです。InPortProvider を継承する必要があります。 Taskクラスの継承はこのサンプル独自のものなので必須ではありません。
コンシューマクラス (InPortTestConsumer)
データポートインターフェース(Push型)のコンシューマクラスです。Push型の場合は InPort側にコンシューマ、OutPort側にプロバイダを生成します。 コンシューマ側で put関数を呼び出した際に、何らかの方法によりプロバイダ側にデータを転送する必要があります。 このサンプルではコンシューマ側の put関数呼び出し時にファイルにデータを書き込み、プロバイダ側でファイルからデータを読み込むことでデータの転送を行っています。
データポートインターフェース(Push型)のコンシューマクラスです。InPortConsumer を継承する必要があります。
プロバイダ、コンシューマ登録関数 (InPortTestInterfaceInit)
OpenRTM-aist のマネージャは「XXX.dll」というダイナミックリンクライブラリをロードした場合に「XXXInit」関数を呼び出します。 このサンプルの場合は「InPortTestInterface.dll」をロードして、以下の「InPortTestInterfaceInit」関数を呼び出します。
確認手順
1. OpenRTM-aistがインストールされている環境を用意します。
2. ビルドのために CMake設定ファイル (CMakeLists.txt) を作成します。
以下は、独自インターフェースのサンプルをビルドするための CMake設定ファイルです。
3. CMake により Visual Studio のプロジェクトファイルを生成します。
4. Visual Studio でビルドします。
ビルド後に、Debugフォルダーに InPortTestInterface.dll が生成されます。
5. rtc.conf を作成します
rtc.conf を作成して、Manager 起動時にロードするモジュールで InPortTestInterface.dll を指定します。
InPortTestInterface.dll が、RTC を実行するディレクトリーと異なる場合は、別途モジュール探索パスを設定します。
6. 上記の rtc.conf を読み込んで RTCを 起動します。
7. ポート接続時にInterface Typeに「test」を指定する。
RTSystemEditor上から Interface Type を指定できます。