RT컴포넌트 작성

데이터포트가 있는 컴포넌트

여기에서는, 데이터포트가 있는 컴포넌트를 두 개 작성해, 두 개의 컴포넌트간에 데이터의 송수신을 해보겠습니다. 작성하는 컴포넌트의 사양은 이하와 같습니다.

  • 컴퍼넌트 1
    • OutPort를 하나 갖는다
    • OutPort의 데이터형은 TimedLong
    • 콘솔로부터 입력한 값을 OutPort로부터 출력
  • 컴퍼넌트 2
    • InPort를 하나 갖는다
    • InPort의 데이터형은 TimeLong
    • 컨피그레이션 파라메터를 하나 갖는다
    • 컨피그레이션 파라메터는 int형
    • 컨피그레이션 파라메터의 디폴트치는 1
    • InPort 변수로부터 읽어낼 때는 파라미터 값을 거친 값을 읽어낸다
    • InPort로부터 읽어낸 값을 콘솔에 출력

rtc-template에 의한 소스 생성

상기의 사양을 가지는 컴퍼넌트를 작성하기 위해 이하와 같은 셸 스크립트를 gen.sh라는 이름으로 준비합니다.

 #!/bin/sh
 
 rtc-template -bcxx      --module-name=ConsoleIn --module-type='DataFlowComponent'      --module-desc='Console input component'      --module-version=1.0 --module-vendor='MyName'      --module-category=example      --module-comp-type=DataFlowComponent --module-act-type=SPORADIC      --module-max-inst=10 --outport=out:TimedLong
 
 rtc-template -bcxx      --module-name=ConsoleOut --module-type='DataFlowComponent'      --module-desc='Console output component'      --module-version=1.0 --module-vendor='MyName'      --module-category=example      --module-comp-type=DataFlowComponent --module-act-type=SPORADIC      --module-max-inst=10 --inport=in:TimedLong      --config="multiply:int:1"

최초의 rtc-template의 실행으로 컴퍼넌트 1:ConsoleInComp, 다음의 rtc-template의 실행으로 컴퍼넌트 2:ConsoleOutComp가 작성됩니다.

 > sh gen.sh 
   File "ConsoleIn.h" was generated.
   File "ConsoleIn.cpp" was generated.
   File "ConsoleInComp.cpp" was generated.
   File "Makefile.ConsoleIn" was generated.
   File "ConsoleInComp_vc8.vcproj" was generated.
   File "ConsoleIn_vc8.vcproj" was generated.
   File "ConsoleInComp_vc9.vcproj" was generated.
   File "ConsoleIn_vc9.vcproj" was generated.
   File "ConsoleIn_vc8.sln" was generated.
   File "ConsoleIn_vc9.sln" was generated.
   File "copyprops.bat" was generated.
   File "user_config.vsprops" was generated.
   File "README.ConsoleIn" was generated.
   File "ConsoleIn.yaml" was generated.
   File "ConsoleOut.h" was generated.
   File "ConsoleOut.cpp" was generated.
   File "ConsoleOutComp.cpp" was generated.
   File "Makefile.ConsoleOut" was generated.
   File "ConsoleOutComp_vc8.vcproj" was generated.
   File "ConsoleOut_vc8.vcproj" was generated.
   File "ConsoleOutComp_vc9.vcproj" was generated.
   File "ConsoleOut_vc9.vcproj" was generated.
   File "ConsoleOut_vc8.sln" was generated.
   File "ConsoleOut_vc9.sln" was generated.
 "copyprops.bat" already exists. Overwrite? (y/n) y
   File "copyprops.bat" was generated.
 "user_config.vsprops" already exists. Overwrite? (y/n) y
   File "user_config.vsprops" was generated.
   File "README.ConsoleOut" was generated.
   File "ConsoleOut.yaml" was generated.

ConsoleIn의 작성

생성된 소스를 편집하고, ConsoleIn 컴퍼포트를 작성해 갈 것입니다.

ConsoleIn.h

이 컴포넌트는 액티브 되었을 때에, 입력 대기를 실시해 입력된 값을 OutPort로부터 출력하는 컴포넌트입니다. 따라서, 액티브 상태 때에 루프 실행되는 onExecute 멤버 함수만 실행하면 좋기 때문에, 생성된 ConsoleIn.h를 이하와 같이 주석처리 되고 있는 onExecute 함수의 주석을 해제합니다.

     : 생략
   // The execution action that is invoked periodically
   // former rtc_active_do()
   virtual RTC::ReturnCode_t onExecute(RTC::UniqueId ec_id);
     : 생략

또, ConsoleIn.h의 아래쪽에, rtc-template로 지정한 OutPort의 변수 선언이 있습니다.

     : 생략
   // DataOutPort declaration
   // <rtc-template block="outport_declare">
   TimedLong m_out;
   OutPort<TimedLong> m_outOut;
   // </rtc-template>

TimedLong m_out라고 선언되고 있는 것이 OutPort에 바인드 되는 변수.

OutPort<TimedLong> m_outOut라고 선언되고 있는 것이 OutPort의 인스턴스입니다.

ConsoleIn.cpp

ConsoleIn의 작성은 간단합니다. 주석처리 되고 있는 onExecute의 주석을 해제해, 이하와 같이 작성합니다.

 RTC::ReturnCode_t ConsoleIn::onExecute(RTC::UniqueId ec_id)
 {
   std::cout << "Please input number: ";
   std::cin >> m_out.data;
   std::cout << "Sending to subscriber: " << m_out.data << std::endl;
   m_outOut.write();
 
   return RTC::RTC_OK;
 }

여기서 행해지고 있는 것은,
  1. cin >> m_out.data로 유저로부터의 입력 대기를 한다.
  2. 입력된 값을, m_out.data(long형)에 저장
  3. 입력된 값의 확인을 위해 표시
  4. m_outOut.write()로 OutPort로부터 데이터를 출력.

ConsoleOut의 작성

ConsoleOut 컴포넌트는 조금 복잡합니다. InPort에 들어 온 데이터에 컨피그레이션 파라메터 multiply에 의해 계산된 값을 저장하지 않으면 안됩니다. 이것은, InPort에 콜백 오브젝트를 세트 하는 방법으로 실현될 수 있습니다.

콜백 오브젝트

콜백 오브젝트란, InPort나 OutPort의 버퍼에 있는 이벤트가 발생했을 때에 불리는operator()가 정의된 오브젝트입니다. 이번은 InPort의 버퍼에 써질 때 값을 변환하기 위해 콜백 OnWriteConvert를 사용합니다.

RTC::OnWriteConvert를 계승해 이하와 같은 클래스를 정의합니다.

 class Multiply
   : public RTC::OnWriteConvert<RTC::TimedLong>
 {
   int& m_mul;
 public:
   Multiply(int& multiply) : m_mul(multiply) {};
   RTC::TimedLong operator()(const RTC::TimedLong& value)
   {
     RTC::TimedLong ret(value);
     ret.data = value.data * m_mul;
     return ret;
   };
 };

ConsoleOut.h

상기의 콜백 클래스를 ConsoleOut.h의 include의 행의 직후에 삽입합니다. 게다가 이 콜백 클래스의 인스턴스를 ConsoleOut 클래스의 멤버 변수로서 선언합니다. 장소는, private의 바로 아래로 합니다.

 private:
   Multiply m_owc;
   int dummy;

이 컴퍼넌트는 액티브화 되었을 때에 InPort로부터 데이터를 읽어들여 표준 출력에 데이터를 표시하는 컴퍼넌트입니다. 따라서, 액티브 상태때에 루프 실행되는 onExecute 멤버 함수만 실행하면 좋기 때문에 생성된 ConsoleOut.h를 이하와 같이 주석처리 되고 있는 onExecute 함수의 주석을 해제합니다.

     : 생략
   // The execution action that is invoked periodically
   // former rtc_active_do()
   virtual RTC::ReturnCode_t onExecute(RTC::UniqueId ec_id);
     : 생략

또, ConsoleOut.h의 아래쪽에 rtc-template로 지정한 컨피그레이션 변수의 선언과 InPort의 변수 선언이 있습니다.

ConsoleOut에서는 InPort의 버퍼로서 RingBuffer를 사용하므로,RingBuffer.h를 인클루드 할 필요가 있습니다. ConsoleOut.h의 선두 부분을로 이하와 같이RingBuffer.h를 인클루드해 주십시오.

 #include <rtm/idl/BasicDataTypeSkel.h>
 #include <rtm/Manager.h>
 #include <rtm/DataFlowComponentBase.h>
 #include <rtm/CorbaPort.h>
 #include <rtm/DataInPort.h>
 #include <rtm/DataOutPort.h>
 #include <rtm/RingBuffer.h> //이것을 추가한다

또, InPort의 선언 부분에서 디폴트에서는InPort<TimedLong> m_inIn되고 있는 곳을 InPort<TimedLong, RTC::RingBuffer> m_inIn와 같이 고쳐 쓰고, InPort가 RingBuffer를 사용하도록 변경해 주십시오.

     : 생략
   // Configuration variable declaration
   // <rtc-template block="config_declare">
   int m_multiply;
     
   // </rtc-template>
   
   // DataInPort declaration
   // <rtc-template block="inport_declare">
   TimedLong m_in;
   InPort<TimedLong, RTC::RingBuffer> m_inIn;

int m_multiply라고 선언되고 있는 것이, 컨피그레이션 「multiply」에 바인드 되는 변수.

TimedLong m_in라고 선언되고 있는 것이, InPort에 바인드 되는 변수.

InPort<TimedLong> m_inIn라고 선언되고 있는 것이, InPort의 인스턴스입니다.

ConsoleOut.cpp

ConsoleOut 클래스의 constructor로 방금전 정의한 Multiply의 인스턴스의 초기화를 추가합니다.

 ConsoleOut::ConsoleOut(RTC::Manager* manager)
   : RTC::DataFlowComponentBase(manager),
     // <rtc-template block="initializer">
     m_inIn("in", m_in),
     
     // </rtc-template>
     m_owc(m_multiply), 
     dummy(0)
※ Remark :
:「m_owc(m_multiply),dummy(0)」의 부분을 추가할 뿐만 아니라, 「m_inIn("in", m_in)」를 「m_inIn("in", m_in) ,」라고 변경해 주십시오 (콤마 「,」의 누락에 주의).|


 게다가 콜백 오브젝트를 InPort에 추가하기 위해, constructor내에서 이하와 같이 기술합니다.

   m_inIn.setOnWriteConvert(&m_owc); //이것을 추가
   // Registration: InPort/OutPort/Service
   // <rtc-template block="registration">
   // Set InPort buffers
   registerInPort("in", m_inIn);

주석 처리되고 있는 onExecute의 주석을 해제해, 이하와 같이 작성합니다.

 RTC::ReturnCode_t ConsoleOut::onExecute(RTC::UniqueId ec_id)
 {
   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;
     }
   usleep(1000);
 
   return RTC::RTC_OK;
 }

여기서 하고 있는 것은,
  1. m_inIn.isNew()로 InPort에 데이터가 들어 오고 있는지 체크한다.
    1. isNew()라고 하는 멤버 함수는 RingBuffer에 정의되고 있는 함수입니다.
  2. 새로운 데이터가 들어가 있으면, m_inIn.read()로 변수에 데이터를 읽어들인다.
  3. 그 데이터(m_in.data)를 표시한다.

    컴파일

작성이 끝나면 이하와 같이 소스를 컴파일 합니다.

 > make -f Makefile.ConsoleIn
 > make -f Makefile.ConsoleOut

compile error가 나왔을 경우는 스펠링 미스 등이 없는지 어떤지 체크해 재차 컴파일을 해주십시오.

실행

  • 네임서버 기동하고 있는 것을 확인합니다.
  • 적절한 rtc.conf*를 작성해 둡니다.
    *적절한 rtc.conf란… 다음은 하나의 예입니다.
     corba.nameservers: localhost
     naming.formats: %h.host_cxt/%n.rtc
  • 두 개의 터미널로부터 ConsoleInComp 및 ConsoleOutComp를 실행합니다.
  • RtcLink를 기동하고, 두 개의 컴퍼넌트를 접속해 액티브합니다.


ConsoleInConsoleOut2.png


 ConsoleIn를 실행한 터미널에서 Please input number: 라고 하는 표시가 나오므로, 적당한 숫자를 입력합니다.

 Please input number: 1
 Sending to subscriber: 1
 Please input number: 2
 Sending to subscriber: 2
 Please input number: 3
 Sending to subscriber: 3

 ConsoleOut를 실행한 터미널에서는, 이하와 같이 표시될 것입니다.

 Received: 1
 TimeStamp: 0[s] 0[ns]
 Received: 2
 TimeStamp: 0[s] 0[ns]
 Received: 3
 TimeStamp: 0[s] 0[ns]

 다음에 RtcLink의 컨피그레이션뷰로 multiply의 값을 10으로 변경해 봅시다. 그러면, ConsoleIn로부터 상기와 같이 입력하면 각각 10배 된 값이 이하와 같이 출력될 것입니다.

 Received: 10
 TimeStamp: 0[s] 0[ns]
 Received: 20
 TimeStamp: 0[s] 0[ns]
 Received: 30
 TimeStamp: 0[s] 0[ns]


Download

latest Releases : 2.0.0-RELESE

2.0.0-RELESE Download page

Number of Projects

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