データ入出力ポートの使用例:SimpleIO
ここでは、Yarbsを用いてOpenRTM-aistのサンプルコンポーネントであるSimpleIOの実装(ConsoleInとConsoleOut)を行います。
RTCの仕様
SimpleIOのRTCは、ConsoleInでは標準入力から整数を入力し、TimedLong型の出力データポートからデータを送信し、ConsoleOutでは、TimedLongの入力ポートからのデータを標準出力に表示します。
ConsoleIn(ConsoleIn.yaml)は、
name: ConsoleIn
version: 2.0.0
vendor: AIST
max_instance: 1
executionType: PeriodicExecutionContext
executionRate: 1.0
description: Console Input Component
category: example
component_type: STATIC
activity_type: PERIODIC
kind: DataFlowComponent
maintainer: Isao Hara
author: Isao Hara(isao-hara@aist.go.jp)
actions:
- OnInitialize: true
- OnExecute: true
dataport:
- name: out
flow: out
type: RTC::TimedLong
description: Outport
となり、ConsoleOut(ConsoleOut.yaml)は,
name: ConsoleOut
version: 2.0.0
vendor: AIST
max_instance: 1
executionType: PeriodicExecutionContext
executionRate: 1.0
description: Console Output Component
category: example
component_type: STATIC
activity_type: PERIODIC
kind: DataFlowComponent
maintainer: Isao Hara
author: Isao Hara(isao-hara@aist.go.jp)
actions:
- OnInitialize: true
- OnExecute: true
dataport:
- name: in
flow: in
type: RTC::TimedLong
description: DataIn
datalistener: DataIn
となります。
RTCのひな形を生成
RTCの仕様を作成後、genRtcCppコマンドでひな形を生成します。
> "C:\Program Files\OpenRTM-aist\setup.bat"
> genRtcCpp ConsoleIn.yaml
> genRtcCpp ConsoleOut.yaml
genRtcCppコマンドを実行後、ConsoleIn, ConsoleOutの構成ファイルは下のようになっています。
ConsoleIn
+-idl
+-include
+-ConsoleIn.h
+-src
+-ConsoleIn.cpp
+-ConsoleInComp.cpp
+-CMakeLists.txt
ConsoleOut
+-idl
+-include
+-ConsoleOut.h
+-src
+-ConsoleOut.cpp
+-ConsoleOutComp.cpp
+-CMakeLists.txt
ここで実装コードのほとんどは、ConsoleIn.cppになります。genRtcCppコマンドでの生成後は、以下のようになっています。
// -*- C++ -*-
/*!
* @file ConsoleIn.cpp
* @author Isao Hara(isao-hara@aist.go.jp)
*
* Copyright (C)
* All rights reserved.
*
*/
#include "ConsoleIn.h"
// Module specification
// <rtc-template block="module_spec">
const char* rtcomponent_spec[] =
{
"implementation_id", "ConsoleIn",
"type_name", "ConsoleIn",
"description", "Console Input Component",
"version", "2.0.0",
"vendor", "AIST",
"category", "example",
"component_type", "STATIC",
"activity_type", "PERIODIC",
"kind", "DataFlowComponent",
"max_instance", "1",
"language", "C++",
"lang_type", "compile",
""
};
/*!
* @brief constructor
* @param manager Maneger Object
*/
ConsoleIn::ConsoleIn(RTC::Manager* manager)
// <rtc-template block="initializer">
: RTC::DataFlowComponentBase(manager),
m_outOut("out", m_out)
// </rtc-template>
{
}
/*!
* @brief destructor
*/
ConsoleIn::~ConsoleIn()
{
}
RTC::ReturnCode_t ConsoleIn::onInitialize()
{
RTC_DEBUG(("onInitialize start"));
RTC_INFO(("ConsoleIn : Console Input Component"));
// Registration: InPort/OutPort/Service
addOutPort("out", m_outOut);
// Confguration Parameters
//---< onInitialize
//--->
RTC_DEBUG(("onInitialize finish"));
return RTC::RTC_OK;
}
RTC::ReturnCode_t ConsoleIn::onExecute(RTC::UniqueId ec_id)
{
//---< onExecute
//--->
return RTC::RTC_OK;
}
extern "C"
{
void ConsoleInInit(RTC::Manager* manager)
{
int i, j;
for (i = 0; strlen(rtcomponent_spec[i]) != 0; i++);
char** spec_intl = new char*[i + 1];
for (j = 0; j < i; j++) {
spec_intl[j] = (char *)rtcomponent_spec[j];
}
spec_intl[i] = (char *)"";
coil::Properties profile((const char **)spec_intl);
manager->registerFactory(profile,
RTC::Create<ConsoleIn>,
RTC::Delete<ConsoleIn>);
}
};
ConsoleInの機能は、アクティベートした状態で、標準入力から整数を入力し、TimedLongのデータポートからそのデータを出力するものですので、onExecuteの部分に実装コードを入力します。genRtcCppコマンドで生成されたソースコードでは、入力ポートは下のように変数定義されます。
RTC::TimedLong m_out;
OutPort<RTC::TimedLong> m_outOut;
即ち、データポート名は、 ''m_<ポート名>Out'' となり、入力ポートへのデータは、''m_<ポート名>''という変数として定義されます。従って、ConsoleInのコアロジックは下のように書くことができます。
RTC::ReturnCode_t ConsoleIn::onExecute(RTC::UniqueId ec_id)
{
//---< onExecute
std::cout << "Please input number: ";
std::cin >> m_out.data;
if (m_out.data == 666) return RTC::RTC_ERROR;
m_outOut.write();
//--->
return RTC::RTC_OK;
}
以上でConsoleInの実装は終了です。
ConsoleOutの場合、ConsoleOut.cppは以下のようになっています。
// -*- C++ -*-
/*!
* @file ConsoleOut.cpp
* @author Isao Hara(isao-hara@aist.go.jp)
*
* Copyright (C)
* All rights reserved.
*
*/
#include "ConsoleOut.h"
// Module specification
// <rtc-template block="module_spec">
const char* rtcomponent_spec[] =
{
"implementation_id", "ConsoleOut",
"type_name", "ConsoleOut",
"description", "Console Output Component",
"version", "2.0.0",
"vendor", "AIST",
"category", "example",
"component_type", "STATIC",
"activity_type", "PERIODIC",
"kind", "DataFlowComponent",
"max_instance", "1",
"language", "C++",
"lang_type", "compile",
""
};
// </rtc-template>
/*!
* @brief constructor
* @param manager Maneger Object
*/
ConsoleOut::ConsoleOut(RTC::Manager* manager)
// <rtc-template block="initializer">
: RTC::DataFlowComponentBase(manager),
m_inIn("in", m_in)
// </rtc-template>
{
}
/*!
* @brief destructor
*/
ConsoleOut::~ConsoleOut()
{
}
RTC::ReturnCode_t ConsoleOut::onInitialize()
{
RTC_DEBUG(("onInitialize start"));
RTC_INFO(("ConsoleOut : Console Output Component"));
// Registration: InPort/OutPort/Service
addInPort("in", m_inIn);
m_inIn.addConnectorDataListener(ON_BUFFER_WRITE,
new DataInDataListener("ON_BUFFER_WRITE", this), false);
// Confguration Parameters
//---< onInitialize
//--->
RTC_DEBUG(("onInitialize finish"));
return RTC::RTC_OK;
}
RTC::ReturnCode_t ConsoleOut::onExecute(RTC::UniqueId ec_id)
{
//---< onExecute
//--->
return RTC::RTC_OK;
}
RTC::ReturnCode_t ConsoleOut::onBufferWrite(RTC::TimedLong data)
{
//---< onBufferWrite
//--->
return RTC::RTC_OK;
}
extern "C"
{
void ConsoleOutInit(RTC::Manager* manager)
{
int i, j;
for (i = 0; strlen(rtcomponent_spec[i]) != 0; i++);
char** spec_intl = new char*[i + 1];
for (j = 0; j < i; j++) {
spec_intl[j] = (char *)rtcomponent_spec[j];
}
spec_intl[i] = (char *)"";
coil::Properties profile((const char **)spec_intl);
manager->registerFactory(profile,
RTC::Create<ConsoleOut>,
RTC::Delete<ConsoleOut>);
}
};
ConsoleOutの入力ポートの仕様では、''datalistener''が定義されています。データリスナーは、ポートの接続、解除、データの到着等のタイミングにコールバック関数を設定する機能です。
Yarbsでは、OpenHRIAudioを実装するために、入力バッファにデータ到着時(ON_BUFFER_WRITE)にデータリスナーを設定することができます。データリスナーに対応するコードは、ConsoleOut.hには、下記のクラス定義とコールバック関数の設定が追加されます。データリスナーのクラス名は、''「datalistenerで設定した文字列」+DataListener''になります。
''クラス定義:''
/*!
* @class DataListener
* @brief
*/
class DataInDataListener
: public ConnectorDataListenerT<RTC::TimedLong>
{
USE_CONNLISTENER_STATUS;
public:
/*!
* @brief constructor
*/
DataInDataListener(const char* name, ConsoleOut *data) : m_name(name), m_obj(data){};
/*!
* @brief destructor
*/
virtual ~DataInDataListener(){};
virtual ReturnCode operator()( ConnectorInfo& info,
RTC::TimedLong& data){
if ( m_name == "ON_BUFFER_WRITE" ) {
/* onBufferWrite */
m_obj->onBufferWrite(data);
}
return NO_CHANGE;
};
ConsoleOut *m_obj;
std::string m_name;
};
''コールバック関数宣言:''
virtual RTC::ReturnCode_t onBufferWrite(RTC::TimedLong data);
また、ConsoleOut.cppには、下のコールバック関数のひな形が追加されます。
''コールバック関数(ひな形):''
''コールバック関数(ひな形):''
RTC::ReturnCode_t ConsoleOut::onBufferWrite(RTC::TimedLong data)
{
//---< onBufferWrite
//--->
return RTC::RTC_OK;
}
ConsoleOutでは、入力ポートに受信した数値を標準出力に出力しますので、コアロジックは、下記のようになります。
RTC::ReturnCode_t ConsoleOut::onExecute(RTC::UniqueId ec_id)
{
//---< onExecute
if (m_inIn.isNew())
{
m_inIn.read();
std::cout << "Received: " << m_in.data << std::endl;
std::cout << "TimeStamp: " << m_in.tm.sec << "[s] ";
std::cout << m_in.tm.nsec << "[ns]" << std::endl;
}
coil::usleep(1000);
//--->
return RTC::RTC_OK;
}
以上で、SimpleIOのConsoleInとConsoleOutの実装は終了です。
RTCのビルド
必要なコードの実装の終了後は、RTCのビルドを行います。
Yarbsには、RTCのビルドのためにrtc_makeというバッチファイルがインストールされています。
Yarbsには、RTCのビルドのためにrtc_makeというバッチファイルがインストールされています。
rtc_makeは、genRtcCppを実行したディレクトリで実行してください。rtc_makeは、デフォルトではRTCのビルドのみを行い、''--install''のオプションが付加されれば、${RTM_ROOT}/Componentsの下または、RTC_PKG_PATHで指定したディレクトリに生成したdll, exeおよびsetup.batをインストールします。
> cd /d C:\work\yarbs_ws
> set RTM_PKG_PATH=%CD%\Components
> mkdir Components
> rtc_make --install ConsoleIn
> rtc_make --install ConsoleOut
実装コードに問題がなければ、RTCのビルドは終了です。
RTCの動作確認
最後に、実装したConsoleInとConsoleOutを起動して動作確認を行います。
OpenRTM-aistのオフィシャルのRTCの操作は、RT SystemEditorまたはrtshellを使いますが、Yarbsでは、rtcmdを使います。
rtcmdは、rtshellの一部の機能+DOS窓用に高度化したインタプリタの機能を持っています。では、rtcmdを起動しましょう。
> rtcmd
Welcome to RtCmd
=>
rtcmdを実行するとネームサーバー(omniNames)の起動状態を確認し、必要であればネームサバ―の起動を行います。
次に、ConsoleInとConsoleOutの起動を行います。
rtcmdでは、外部アプリケーションをcmd.exe経由で呼び出す機能がありますので、それを使用します。
また、Yarbsには、インストールしたRTCの起動用バッチファイルとしてrtcrun.batというバッチファイルがインストールされています。このバッチファイルは、"%RTM_PKG_PATH%";"%RTM_ROOT%/Components" の順に引数で指定したディレクトリ、実行ファイルの起動を行うための補助ファイルです。
rtcmdでは、外部アプリケーションをcmd.exe経由で呼び出す機能がありますので、それを使用します。
また、Yarbsには、インストールしたRTCの起動用バッチファイルとしてrtcrun.batというバッチファイルがインストールされています。このバッチファイルは、"%RTM_PKG_PATH%";"%RTM_ROOT%/Components" の順に引数で指定したディレクトリ、実行ファイルの起動を行うための補助ファイルです。
ConsoleInおよびConsoleOutは、それぞれ、%RTM_PKG_PATH%/ConsoleIn/と%RTM_PKG_PATH%/ConsoleIn/にインストールされますので、rtcmdのプロンプト内で、下のように入力すれば、2つのRTCを起動することができます。
=> start rtcrun ConsoleIn ConsoleIn
=> start rtcrun ConsoleOut ConsoleOut
それぞれのRTCが起動したかどうか(ネームサーバーに登録されたかどうか)を確認するためのlistコマンドを実行します。
=> list
===== RTCs =====
1 : DESKTOP-SRBNMG7.host_cxt/ConsoleIn0.rtc
2 : DESKTOP-SRBNMG7.host_cxt/ConsoleOut0.rtc
=>
RTCの起動が確認出来たら、ポートの接続を行い、アクティベートしてください。rtcmdでは、コマンドや引数の補完機能が使えますので、コマンドもRTC名なども途中まで入力後<TAB>キーを押下すれば補完を行います。
=> connect DESKTOP-SRBNMG7.host_cxt/ConsoleIn0.rtc:out DESKTOP-SRBNMG7.host_cxt/ConsoleOut0.rtc:in
=> list -l
===== RTCs =====
1 : DESKTOP-SRBNMG7.host_cxt/ConsoleIn0.rtc
[out]<- out(RTC/TimedLong)
+- ['DESKTOP-SRBNMG7.host_cxt/ConsoleIn0.rtc_out_DESKTOP-SRBNMG7.host_cxt/ConsoleOut0.rtc_in']
2 : DESKTOP-SRBNMG7.host_cxt/ConsoleOut0.rtc
[in] -> in(RTC/TimedLong)
+- ['DESKTOP-SRBNMG7.host_cxt/ConsoleIn0.rtc_out_DESKTOP-SRBNMG7.host_cxt/ConsoleOut0.rtc_in']
=> activate all
=> list
===== RTCs =====
1 : DESKTOP-SRBNMG7.host_cxt/ConsoleIn0.rtc*
2 : DESKTOP-SRBNMG7.host_cxt/ConsoleOut0.rtc*
=>
以上のプロセスで、データポートの接続とアクティベートは終了です。ConsoleInを起動したウィンドウから数値を入力し、ConsoleOutに出力されれば動作完了です。
下記のコマンドでディアクティベートし、RTCを終了させてください。
=> deactivate all
=> terminate all
下に実行例を示します。
この動画は、下記のlauncherファイルを使って起動しています。
start_graph
start rtcrun ConsoleIn ConsoleIn
wait_for 10 -c %h.host_cxt/ConsoleIn0
start rtcrun ConsoleOut ConsoleOut
wait_for 10 -c %h.host_cxt/ConsoleOut0
wait_for 3
connect %h.host_cxt/ConsoleIn0.rtc:out %h.host_cxt/ConsoleOut0.rtc:in
wait_for 3
activate all