データ入出力ポートの使用例:SimpleIO(Python)
Simple Wiki Based Contents Management System
ソフトウェア関連 >> OpenRTM-aist のRTC開発システム(Yarbs) >> RTCの実装 >> PythonによるRTCの実装 >> データ入出力ポートの使用例:SimpleIO(Python)

データ入出力ポートの使用例: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の仕様を作成後、genRtcPythonコマンドでひな形を生成します。
 > "C:\Program Files\OpenRTM-aist\setup.bat"
 > genRtcPython ConsoleIn.yaml
 > genRtcPython ConsoleOut.yaml
genRtcPythonコマンドを実行後、ConsoleIn, ConsoleOutの構成ファイルは下のようになっています。
  ConsoleIn
     +-idl
     +-scripts
        +-ConsoleIn.py
        +-DataFlowRTC_Base.py
     +-ConsoleIn.exe
     +-idlcompile.bat
     +-rtc.conf

  ConsoleOut
     +-idl
     +-scripts
        +-ConsoleOut.py
        +-DataFlowRTC_Base.py
     +-ConsoleOut.exe
     +-idlcompile.bat
     +-rtc.conf
ここで実装コードは、ConsoleIn.py, ConsoleOut.pyに実装します。
ConsoleIn.exe, ConsoleOut.exeは、それぞれPythonのLaucherとなっており、環境変数PYTHON_EXEにpython.exeのパスを設定すれば、実行ファイルのように動作します。
また、idlcompile.batは、IDLコンパイラの呼出し用バッチファイルであり、この例では使用しません。

RTCの実装

genRtcpythonコマンド実行後後、ConsoleIn.py, ConsoleOut.pyは以下のようになっています。
''ConsoleIn:''
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# -*- Python -*-

"""
 @license the MIT License

 Copyright(C) 2018 Isao Hara,AIST,JP
 All rights reserved.

"""
from DataFlowRTC_Base import *

#---< local_def
#--->

##
# @class ProjectName
# 
# 
class ConsoleIn(DataFlowRTC_Base):
  ##
  # @brief constructor
  # @param manager Maneger Object
  # 
  def __init__(self, manager):
    DataFlowRTC_Base.__init__(self, manager)
#---< init
#--->

  ##
  #
  # The initialize action (on CREATED->ALIVE transition)
  # formaer rtc_init_entry() 
  # 
  # @return RTC::ReturnCode_t
  # 
  #
  def onInitialize(self):
    DataFlowRTC_Base.onInitialize(self)
#---< OnInitialize
#--->
    return RTC.RTC_OK


  #####
  #   onExecute
  #
  def onExecute(self, ec_id):
#---< OnExecute
#--->
    return RTC.RTC_OK



#---< local_def2
#--->
#########################################
g_rtc_data={'ProjectName': 'ConsoleIn',
 'actions': [{'OnInitialize': True}, {'OnExecute': True}],
 'activity_type': 'PERIODIC',
 'author': 'Isao Hara(isao-hara@aist.go.jp)',
 'category': 'example',
 'component_type': 'STATIC',
 'dataport': [{'description': 'Outport',
               'flow': 'out',
               'name': 'out',
               'type': 'RTC::TimedLong'}],
 'description': 'Console Input Component',
 'executionRate': 1.0,
 'executionType': 'PeriodicExecutionContext',
 'kind': 'DataFlowComponent',
 'maintainer': 'Isao Hara',
 'max_instance': 1,
 'name': 'ConsoleIn',
 'vendor': 'AIST',
 'version': '2.0.0'}

#########################################
#  Initializers
#
def main():
  global g_rtc_data
  mgr = rtc_init(ConsoleIn, 
          #rtc_yaml=os.path.join(os.path.dirname(__file__), 'ConsoleIn.yaml'),
          rtc_data=g_rtc_data)
  mgr.runManager()

if __name__ == "__main__":
  main()

''ConsoleOut:''
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# -*- Python -*-

"""
 @license the MIT License

 Copyright(C) 2018 Isao Hara,AIST,JP
 All rights reserved.

"""
from DataFlowRTC_Base import *

#---< local_def
#--->

##
# @class ProjectName
# 
# 
class ConsoleOut(DataFlowRTC_Base):
  ##
  # @brief constructor
  # @param manager Maneger Object
  # 
  def __init__(self, manager):
    DataFlowRTC_Base.__init__(self, manager)
#---< init
#--->

  ##
  #
  # The initialize action (on CREATED->ALIVE transition)
  # formaer rtc_init_entry() 
  # 
  # @return RTC::ReturnCode_t
  # 
  #
  def onInitialize(self):
    DataFlowRTC_Base.onInitialize(self)
#---< OnInitialize
#--->
    return RTC.RTC_OK


  #####
  #   onExecute
  #
  def onExecute(self, ec_id):
#---< OnExecute
#--->
    return RTC.RTC_OK

  #####
  #   onData
  #
  def onData(self, name, data):
    print(name, data)
#---< OnData
#--->

    return RTC.RTC_OK


#---< local_def2
#--->
#########################################
g_rtc_data={'ProjectName': 'ConsoleOut',
 'actions': [{'OnInitialize': True}, {'OnExecute': True}],
 'activity_type': 'PERIODIC',
 'author': 'Isao Hara(isao-hara@aist.go.jp)',
 'category': 'example',
 'component_type': 'STATIC',
 'dataport': [{'datalistener': 'DataIn',
               'description': 'DataIn',
               'flow': 'in',
               'name': 'in',
               'type': 'RTC::TimedLong'}],
 'description': 'Console Output Component',
 'executionRate': 1.0,
 'executionType': 'PeriodicExecutionContext',
 'kind': 'DataFlowComponent',
 'maintainer': 'Isao Hara',
 'max_instance': 1,
 'name': 'ConsoleOut',
 'vendor': 'AIST',
 'version': '2.0.0'}

#########################################
#  Initializers
#
def main():
  global g_rtc_data
  mgr = rtc_init(ConsoleOut, 
          #rtc_yaml=os.path.join(os.path.dirname(__file__), 'ConsoleOut.yaml'),
          rtc_data=g_rtc_data)
  mgr.runManager()
if __name__ == "__main__":
  main()

上記のコードでわかると思いますが、ConsoleIn.py, ConsoleOut.pyの両方ともにonInitializedのメソッドでデータポートに関するコードがありません。これは、ConsoleIn(まはたConsolwOut)の親クラスであるDataFlowRTC_Baseでデータポート(およびサービスポート)の設定を行っているからです。
Yarbsでは、Pythonの実装コードは、C++の場合と比較して、非常に簡素化されていることがわかると思います。
では、ConsoleIn, ConsoleOutにコアロジックを実装していきます。

ConsoleIn

ConsoleInの機能は、アクティベートした状態で、標準入力から整数を入力し、TimedLongのデータポートからそのデータを出力するものです。
したがって、その機能をonExecuteの部分に実装コードを追加します。DataFlowRTC_Baseでは、タイムアウト付きの標準入力関数 input_with_timeoutが定義されていますので、ここでは、その関数を使用します。
また、データ出力ポートのnameが ''out'' の場合はデータポートは、 self._outOut となり、データポートのデータ変数は、self._d_outとなります。
#---< OnExecute
    try:
      data = input_with_timeout("Please input: ", 10)
      self._d_out.data = int(data)
      OpenRTM_aist.setTimestamp(self._d_out)
      print("Sending to subscriber: ", self._d_out.data)
      self._outOut.write()
    except TimeoutExpired:
      pass
    except InputTerminated:
      self.deactivate(ec_id)
      print("...Deactivate")
    except:
      traceback.print_exc()
#--->
この実装例では、10秒ごとにタイムアウトが発生して、"Please input:"とう文字列が表示されます。以上で、ConsoleInの実装は終了です。

ConsoleOut

ConsoleOutの機能は、アクティベートの状態で、入力ポートからデータを受信したときに、そのデータを表示するものです。
したがって、ConsoleOutのonExecutionは、下のように実装することができます。
データ入力ポートのnameが ''in'' の場合はデータポートは、 self._inIn となります。
#---< OnExecute
    try:
      if self._inIn.isNew():
        data = self._inIn.read()
        print("Received: ", data)
        print("Received: ", data.data)
        print("TimeStamp: ", data.tm.sec, "[s] ", data.tm.nsec, "[ns]")
    except:
      traceback.print_exc()
#--->
以上でConsoleOutの実装は終了です。

RTCの動作確認

RTCの実装終了後は、動作確認を行います。PythonによるRTCの実装の場合には、コンパイルなどの作業はありません。
したがって、適切な環境設定後、即座に実行することができます。
ここでもrtcmdを使って動作確認を行います。まずは、下のようにrtcmdを起動します。
 > "C:\Program Files\OpenRTM-aist\setup,bat"
 > rtcmd
 => start_graph
次にConsoleInとConsoleOutのRTCを起動します。RTCの起動は、startコマンドを使います。そして、データポートの接続を行ってください。
 => start ConsoleIn\ConsoleIn.exe
 => start ConsoleOut\ConsoleOut.exe
 => connect %h.host_cxt/ConsoleIn0.rtc:out %h.host_cxt/ConsoleOut0.rtc:in
 => activate all
RTCのアクティベートを行うとConsoleInの方に入力プロンプトが表示されますので、適当な数値を入力します。ENTERを押下後、ConsoleOutに入力した数値が表示されることを確認してください。
以上で、動作確認は終了です。下記のコマンドでRTCとrtcmdを終了させます。
 => deactivate all
 => terminate all
 => bye
上記の一連の動作を下の動画にしていますので参照してください。

この動画は、下記のlauncherファイルを使って起動しています。
start_graph
start ConsoleIn/ConsoleIn
wait_for 10 -c %h.host_cxt/ConsoleIn0
start 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