top of page

Defining Own Data Type

on OpenRTM-aist C++ for Windows

 Windows OS上のOpenRTM-aist C++版で独自データ型の使用方法を解説します。

 

更新履歴

 

  • 20/06/2014 OpenRTM-aist Python版との共存に関する注意を追記しました。

4. IDLファイルの登録

 

 作成したIDLファイルは、RTC Builderに登録することで、デフォルトのデータ型と変わらず使用することができます。


それでは、RTC BuilderにIDLファイルを登録します。

RTC Builderを起動し、メニューバーから(Window)」 > 「設定(Preferences)」と選択して下さい。

すると、設定ダイアログが立ちあがります。

 

①のRtcBuilderを選択し、②の新規(New)ボタンを押します。

すると、フォルダー参照ダイアログが現れるので、そこから先ほど作成したMyIDLフォルダを指定してOKバタンを押して下さい。

1. はじめに

 

 RTコンポーネント間の通信に使用するデータポートの型は、開発者が独自に定義することができます。

ここでは、独自データ型の定義方法と、それを使用したRTCの作成方法を学びます。

独自データ型を使用したRTコンポーネントの作成手順は以下のようになります。

 

 (1) 独自データ型が記述されたIDL(インターフェイス定義言語)ファイルの作成

 (2) RTC Builderに独自データ型のIDLファイルの登録

 

 

それでは早速、RTコンポーネントを作成してみましょう。

 

*IDLコンパイラに関する注意

■Windows OS 64bitの場合

IDLコンパイラのために64bitのPythonが必要です。ここを参考に環境構築を行って下さい。

 

 ■OpenRTM-aist Python版と併用している場合

Pythonの環境変数がOpenRTM関連の変数より前に登録されていると、IDLファイルがコンパイルできません。

Pythonの環境変数をOpenRTM関連の変数より後ろに登録しなおしてください。

7. おわりに

 

 これでOpenRTM-aist C++版で独自データ型を使用できるようになりました。

この例では単純なデータ構造でしたが、送信したいデータに適した型を設計することで、データ通信とその後の処理が簡単になります。

今回はデータの長さを格納できるようにすることで、受け取り側のRTCがいちいちデータの長さを数えなくて済むようにしてみました。

 

 このように、独自データ型は適切に用いれば非常に強力な道具となります。

しかし、独自データ型で定義したデータポートは、他のRTCと交互性を保ちにくという欠点もあります。

独自データ型は、デフォルトのデータ型では表現できないときにのみ使用するよう心がけて下さい。

MyIDLフォルダを指定すると①のようにフォルダへのパスが入力されます。

次に、②のOKボタンを押せばしてください。

 

入力後RTC Builderを再起動します。これで、IDLファイルの登録は完了です。

5. テストRTCの作成

 

 先ほど定義したMyLongSeq型を利用したRTCを作成します。

今回は、MyIDLTestConsoleInとMyIDLTestConsoleOutを作成します。

 

MyIDLTestConsoleInはMyLongSeq型のコンソールから入力されたデータを出力します。

MyIDLTestConsoleOutはMyLongSeq型のデータを受け取り、コンソール上に表示します。

 

いつものように、RTC Builderを使用して各RTCを作成します。

6. 動作確認

 

 いつものように、作成したRTCを実行し、動作確認を行って下さい。

下図のように、3つの値が受け渡しができていれば成功です。

ロボコンマガジン
  1. #include "BasicDataType.idl"

  2.  

  3. module Test

  4. {

  5.   struct MyLongSeq

  6.   {

  7.      RTC::Time tm;

  8.      sequence<long> data;

  9.      long data_length;

  10.   };

  11. };

  • 1行目

BasiDataType.idlをincludeします。

これで、RTC::Time型のタイムスタンプが使用できます。

 

  • 3-11行目

Testというモジュールの中に、MyLongSeqという型を定義します。

モジュールの中には複数の型や、サービスを定義することができます。

わかりやすいモジュール名を設定し、型を定義して下さい。

 

  • 5-10行目

MyLongSeqというデータ型を定義します。

このデータ型は、以下のメンバを持ちます。

 

RTC::Time型のタイムスタンプ tm

longのsequence型の変数 data

long型の変数 data_length

 

このデータ型は、TimedLongSeq型のdataの長さを格納できるように拡張したデータ型になっています。

data_lengthはdataの長さを格納することを想定しています。

 

入力したら、コードはTestDataType.idlというファイル名で、MyIDLフォルダに保存してください。

*拡張子はtxtではなくidlで保存してください。

 

これで、独自データ型を定義したIDLファイルの完成です。

3. IDLファイルの作成

 

 独自データ型はBasicDataTypeのように、IDLファイルに定義することで使用できます。

独自データ型を記述したIDLファイルを作成しましょう。

 

3.1 保存フォルダの作成

 

 IDLファイルを作成する前に、「MyIDL」というフォルダを作成して下さい。

ここでは、以下の場所に作成したして説明します。

 

C:\Users\ユーザ名\Documents\OpenRTM\MyIDL

 

IDLファイルは一度作成したら何度も使用することになるので、頻繁に移動する必要がない場所に保存してください。

 

3.2 BasicDataType.idlのコピー

 

 繰り返しになりますが、RTC::Time型のタイムスタンプを持つことが推奨されています。

そこで、RTC::Time型が定義されている、「BasicDataType.idl」というファイルを先ほど作成したMyIDLフォルダにコピーします。

デフォルトでは以下のフォルダに格納されていますので、MyIDLフォルダにコピーして下さい。

 

C:\Program Files\OpenRTM-aist\1.1\rtm\idl

 

3.3 TestDataType.idlの作成

 

 TestDataType.idlというIDLファイルを作成します。

以下のコードを、適当なテキストエディタで入力してください。

2. BasicDataType

 

 普段、私たちが使用しているRTMのデフォルトのデータ型は「BasicDataType.idl」というファイルに記述されています。

BasicDataType.idlはデフォルトでは以下の場所にあります。

 

C:\Program Files\OpenRTM-aist\1.1\rtm\idl

 

このBasicDataType.idlから、IDLファイルの記述方法を学びます。

以下のコードはBasicDataType.idlから一部抜粋したものです。

  1. module RTC {

  2.  

  3.   struct Time

  4.   {

  5.         unsigned long sec;    // sec

  6.         unsigned long nsec;   // nano sec

  7.   };

  8.  

  9.   struct TimedLong

  10.   {

  11.     Time tm;

  12.     long data;

  13.   };

  14.  

  15.   struct TimedLongSeq

  16.   {

  17.     Time tm;

  18.     sequence<long> data;

  19.   };

  20. };

  • 1-20行目

RTCというモジュールの中に、Time、TimedLong、TimedLongSeqという型が定義されています。

モジュールの中には複数のデータ型、ここにはありませんがサービスを定義することもできます。

普段、私たちが使用しているデフォルトのデータ型は、RTCというモジュールに属していることがわかります。

 

  • 3-7行目

Timeというデータ型を定義しています。

このデータ型は、以下のメンバを持ちます。

 

unsigned long型のsec

unsigned long型のnsec

 

この型はタイムスタンプとして使用されます。

特に理由がない場合は、データ型はこのタイムスタンプを持つことを推奨されています。

 

  • 9-13行目

TimedLongというデータ型を定義しています。

このデータ型は、以下のメンバを持ちます。

 

Time型のtm

long型のdata

 

この型はConsolein、ConsoleOutにも使用され馴染み深いと思います。

Timed~という型はTime型のタイムスタンプを持つことを意味することがわかります。

このように、基本型以外に独自に定義したデータ型を使用して、新たに型を定義することができます。

 

  • 15-19行目

TimedLongSeqというデータ型を定義しています。

このデータ型は、以下のメンバを持ちます。

 

Time型のtm

longのsequence型のdata

 

dataがsequence型である以外は、TimedLong型と一緒です。

~seqというのはsequence型のデータを持つことを意味することがわかります。

*sequence型がどのようなものかわからない方は、配列のようなものだと考えて下さい。

 

このように、IDLファイルはC言語の構造体のように記述できるので、記述に際して、特に戸惑うことはないと思います。

IDLファイルでは、以下の様な型を使用することができます。

short

long

unsinged short

unsinged long

float

double

char

wchar

boolean

octet

longlong

ulonglong

sequence<T>

short型整数

long型整数

符号なしshort型整数

符号なしlong型整数

単精度浮動小数点

倍精度浮動小数点

文字型

wchar文字型

bool型

octet型

longlong型整数

符号なしlonglong型整数

Tのsequence型

5.1 MyIDLTestConsoleInの作成

 

 MyIDLTestConsoleInのテンプレートの設定は以下のようになります。

 

  • モジュール名  MyIDLTestConsoleIn

  • ベンダ名  あなたの名前

  • モジュールカテゴリ  TEST

  • 実行周期  1000

  • アクションコールバック

onExecute

  • ポート名(OutPort)  keyboard

  • データ型  Test::MyLongSeq

  • 変数名  data

  • 表示位置  RIGHT

  • 言語 C++

 

 ここで、いつもであればCMakeを使用してビルド環境の設定を行います。

しかし、独自定義のIDLファイルを使用する場合は、cmakeファイルを編集する必要があります。

 

  • ~\MyIDLTestConsoleIn\cmakeフォルダ内の「utils.cmake」

下記にutils.cmakeのコードを抜粋して示します。

「foreach(_item ${${_list}})」の部分を赤字のように書き換えて下さい。

# Filter a list to remove all strings matching the regex in _pattern. The
# output is placed in the variable pointed at by _output.
macro(FILTER_LIST _list _pattern _output)
    set(${_output})
    foreach(_item ${@_list@})
        if("${_item}" MATCHES ${_pattern})
            set(${_output} ${${_output}} ${_item})
        endif("${_item}" MATCHES ${_pattern})
    endforeach(_item)
endmacro(FILTER_LIST)

■64bitのWindows OSを使用している場合

Pythonのバージョンを指定する必要があります。

 

  • ~\MyIDLTestConsoleIn\idlフォルダ内の「CMakeLists.txt」

下記にCMakeLists.txtのコードを抜粋して示します。

「COMMAND python」の部分を赤字のように書き換えて下さい。

add_custom_command(OUTPUT ${${_idl_srcs_var}}
        COMMAND py -2 ${OPENRTM_DIR}/bin/${_rtm_skelwrapper_command} --include-dir= --skel-suffix=Skel --stub-suffix=Stub --idl-file=${_idl}.idl
        COMMAND ${OPENRTM_IDLC} ${OPENRTM_IDLFLAGS} ${_idl_file}
        WORKING_DIRECTORY ${CURRENT_BINARY_DIR}
        DEPENDS ${_idl_file}
        COMMENT "Compiling ${_idl_file}" VERBATIM)

これで、いつものようにCMakeを用いてビルド環境の設定が行えます。

CMakeを用いてソリューションファイルを作成してください。

 

 MyIDLTestConsoleIn.cppの コードを以下に示します。

以下のコードは抜粋となります。生成したテンプレートに当てはめて下さい。

  1. RTC::ReturnCode_t MyIDLTestConsoleIn::onExecute(RTC::UniqueId ec_id)

  2. {

  3.   m_data.data_length = 3;

  4.   m_data.data.length(m_data.data_length);

  5.  

  6.   for(int i = 0; i < m_data.data_length; i++)

  7.   {

  8.       std::cout << "Please input (Long)data " << i << " of 3" << std::endl;

  9.       std::cin >> m_data.data[i];

  10.   }

  11.  

  12.   setTimestamp(m_data);

  13.   m_dataOut.write();

  14.  

  15.   return RTC::RTC_OK;

  16. }

  • 3行目

sequence型のデータの長さを格納する変数に、データの長さを代入しています。

 

  • 4行目

sequence型のデータ領域を確保します。

今回は、Long型のデータが3つ分確保されます。

このように、sequence型のデータをC++言語で使用するには、予め必要なデータ領域を確保する必要があります。

 

  • 6-10行目

sequence型のデータ領域に1つずつ数値を格納します。

sequence型のデータの長さ分だけ繰り返すので、今回は3回繰り返します。

 

  • 8行目

Long型のデータを入力するようユーザに促します。

 

  • 9行目

キーボードに入力されたデータを、sequence型のデータ領域に1つずつ格納します。

sequence型のデータは、配列のように[ ]の中にインデックスを入力してアクセスできます。

 

  • 12行目

タイムスタンプを取得し、格納します。

setTimestamp()を用いることで、タイムスタンプを取得し、格納することができます。

 

  • 13行目

データを出力します。

 

以上のコーディングが終了したら、Releaseモードに変更した後、F7ボタンを押してビルドして下さい。

もしエラーが出た場合は、コードの記述ミスを探して修正して下さい。

 

無事ビルドできた場合は、~\MyIDLTestConsoleIn\build\src\Releaseに「MyIDLTestConsoleInComp.exe」が生成されているはずです。

~\MyIDLTestConsoleInにある「MyIDLTestConsoleIn.conf」と「rtc.conf」を、

実行ファイルがある~\MyIDLTestConsoleIn\build\src\Releaseにコピーして下さい。

 

これで、MyIDLTestConsoleIn RTCの完成です。

 

5.2 MyIDLTestConsoleOutの作成

 

 MyIDLTestConsoleOutのテンプレートの設定は以下のようになります。

 

  • モジュール名  MyIDLTestConsoleOut

  • ベンダ名  あなたの名前

  • モジュールカテゴリ  TEST

  • 実行周期  1000

  • アクションコールバック

onExecute

  • ポート名(InPort)  receive

  • データ型  Test::MyLongSeq

  • 変数名  data

  • 表示位置  LEFT

  • 言語 C++

 

 先ほどと同様に、cmakeファイルを編集してください。

編集後、CMakeを用いてソリューションファイルを作成してください。

 

 MyIDLTestConsoleOut.cppの コードを以下に示します。

以下のコードは抜粋となります。生成したテンプレートに当てはめて下さい。

  1. RTC::ReturnCode_t MyIDLTestConsoleOut::onExecute(RTC::UniqueId ec_id)

  2. {

  3.   if(m_dataIn.isNew())

  4.   {

  5.     m_dataIn.read();

  6.  

  7.     for(int i = 0; i < m_data.data_length; i++)

  8.     {

  9.         std::cout << "data " << i << ":" << m_data.data[i] << std::endl;

  10.     }

  11.     

  12.     std::cout << "sec: " << m_data.tm.sec << "nsec: " << m_data.tm.nsec << std::endl;

  13.   }

  14.  

  15.   return RTC::RTC_OK;

  16. }

  • 3-13行目

m_dataInというInPortオブジェクトのisNewメソッドで新しいデータがあるか調べます。

データを取得した場合、コンソールに表示します。

 

  • 5行目

m_dataInのreadメソッドで受け取った値を読み込みます。

 

  • 7-10行目

dataを1つずつコンソールに出力します。

data_lengthを用いて、ループ回数を決定しています。

 

  • 12行目

受け取ったタイムスタンプをコンソールに表示します。

タイムスタンプは秒とナノ秒を格納する変数に分かれて格納されています。

 

 

以上のコーディングが終了したら、Releaseモードに変更した後、F7ボタンを押してビルドして下さい。

もしエラーが出た場合は、コードの記述ミスを探して修正して下さい。

 

無事ビルドできた場合は、~\MyIDLTestConsoleOut\build\src\Releaseに「MyIDLTestConsoleOutComp.exe」が生成されているはずです。

~\MyIDLTestConsoleOutにある「MyIDLTestConsoleOut.conf」と「rtc.conf」を、

実行ファイルがある~\MyIDLTestConsoleOut\build\src\Releaseにコピーして下さい。

 

これで、MyIDLTestConsoleOut RTCの完成です。

MyIDLTestConsoleIn RTC

MyIDLTestConsoleOut RTC

bottom of page