TIPS을 소개합니다.
OpenRTM-aist-0.4.2를 windows vista상에서 실행하기 위해서, 스타트 메뉴로부터 NameServise를 실행해도 RTSystemEditor에서 접속할 수 없는 경우가 있습니다.
C:\Program Files\OpenRTM-aist\0.4\bin\rtm-naming.bat 를 이하와 같이 변경해 환경 변수 OMNIORB_USEHOSTNAME를 무효화해 주십시오.
set OMNIORB_USEHOSTNAME=localhost ↓ rem set OMNIORB_USEHOSTNAME=localhost
ARTLinux상에서 OpenRTM-aist-0.4.0으로 작성한 RT컴포넌트를 리얼타임으로 실행하기 위한 방법을 설명합니다. OpenRTM-aist에서는 loadable module로서 작성한 실행 컨텍스트(ExecutionContext)을 동적으로 로드해, 컴포넌트에 바인드 하는 것으로 RT컴포넌트의 동작을 제어할 수 있습니다.
OpenRTM-aist-0.4.0에서는 컴포넌트의 실행 주체(≒스렛드)는 ExecutionContext라고 하는 명시적으로 분리된 오브젝트가 되고 있어 상기와 같은 방법에서는 리얼타임화할 수 없습니다.
순서로서는, 이하와 같이 됩니다.이하의 링크로부터 ARTExecutionContext의 소스를 다운로드합니다.
파일을 적당한 디렉토리에 압축을 해제해 빌드합니다. 부속의 Makefile는/usr/lib/art_syscalls.o가 존재하는 것으로서 기술되고 있습니다. art_syscalls.o가 다른 장소에 있는 경우는 Makefile를 수정해 주십시오.
$ tar xvzf ARTExecutionContext.tgz $ cd ARTExecutionContext/ $ make
작성 끝난 컴포넌트의 *Comp.cpp 의 앞부분을 변경합니다.
manager->load("ArtExecutionContext.so", "ArtExecutionContextInit"); setModuleInitProc(MyModuleInit);
컴포넌트를 재컴파일 합니다.
$ make -f Makefile.yourcomp
rtc.conf에 이하와 같이(1)(4)를 추가합니다.
corba.nameservers: localhost:9876 naming.formats: Sample/%n.rtc logger.log_level: TRACE exec_cxt.periodic.type: ArtExecutionContext # (1) exec_cxt.periodic.rate: 200 # (2) manager.modules.load_path: ./ # (3) manager.modules.abs_path_allowed: yes # (4)
위에서 작성한 ARTExecutionContext.so를 컴포넌트 실행 디렉토리에 복사합니다.
$ cd YourComponentDir $ cp ARTExecutionContext/ARTExecutionContext.so .
컴포넌트를 실행합니다.
$ YourComponentComp
여기에서는, ArtExecutionContext를 이용한 간단한 샘플을 소개합니다.(栗原 제공)
rtc-template에서 컴포넌트의 모형을 생성합니다.
이하와 같이 gen.sh와 같은 파일을 생성하면, 재차 모형을 생성할 때에 편리합니다.
$ mkdir ArtEcSample $ cd ArtEcSample $ vi gen.sh rtc-template -bcxx --module-name=art_ec_test --module-desc=art ec test component --module-version=1.0.0 --module-vendor=S.Kurihara --module-category=Category --module-comp-type=STATIC --module-act-type=DataFlowComponent --module-max-inst=1 $ sh gen.sh File "art_ec_test.h" was generated. File "art_ec_test.cpp" was generated. File "art_ec_testComp.cpp" was generated. File "Makefile.art_ec_test" was generated. File "README.art_ec_test" was generated.
art_ec_test.h의 onExecute(), onDeactivate() 의 주석을 해제합니다.
이번은 onExecute() 내에서 gettimeodda()에서 취득한 시간을 파일에 쓰는 컴포넌트를 작성합니다.
[art_ec_test.cpp]
// -*- C++ -*- /*! * @file art_ec_test.cpp * @brief art ec test component * @date $Date$ * * $Id$ */ #include "art_ec_test.h" #include <fstream> #include <sys/time.h> // 시간을 파일에 쓰기 위한 오브젝트 ofstream out; static const char* art_ec_test_spec[] = { "implementation_id", "art_ec_test", "type_name", "art_ec_test", "description", "art ec test component", "version", "1.0.0", "vendor", "S.Kurihara", "category", "Category", "activity_type", "DataFlowComponent", "max_instance", "1", "language", "C++", "lang_type", "compile", "" }; art_ec_test::art_ec_test(RTC::Manager* manager) : RTC::DataFlowComponentBase(manager), dummy(0) { // File open out.open("cycle.log"); } art_ec_test::~art_ec_test() { } /* RTC::ReturnCode_t art_ec_test::onActivated(RTC::UniqueId ec_id) { return RTC::RTC_OK; } */ RTC::ReturnCode_t art_ec_test::onDeactivated(RTC::UniqueId ec_id) { // File close out.close(); return RTC::RTC_OK; } RTC::ReturnCode_t art_ec_test::onExecute(RTC::UniqueId ec_id) { /****** 시간 취득과 파일에 쓰기 ***********/ timeval tv; gettimeofday(&tv,0); out << tv.tv_usec << std::endl; /************************************************/ return RTC::RTC_OK; } extern "C" { void art_ec_testInit(RTC::Manager* manager) { RTC::Properties profile(art_ec_test_spec); manager->registerFactory(profile, RTC::Create<art_ec_test>, RTC::Delete<art_ec_test>); } };
art_ec_testComp.cpp의 main() 함수내의manager->setModuleInitProc(MyModuleInit);의 위에 이하의 1행을 추가합니다.
manager->load("ArtExecutionContext.so", "ArtExecutionContextInit");
ART용 ExecutionContext?를 이용하기 위해서는 ARTExecutionContext?.so가 컴포넌트 실행 디렉토리에 존재해야 합니다.
$ cd ArtEcSample $ cp ~/ARTExecutionContext/ARTExecutionContext.so .
이하의 방법으로 컴포넌트를 실행 후, Activate, DeActivate를 실시하면 cycle.log라고 하는 파일이 생깁니다.
$ ./art_ec_testComp
아래는 위의 컴포넌트에서 계측한 매주기의 차분 시간의 데이터입니다.
표시되고 있는 값은 마이크로 세컨드입니다.
[exec_cxt.periodic.rate: 1000] 1015 1004 997 1001 984 1033 956 1024 991 981 995 1026 1006 999 973 1009 1012 999 1010 1002
[exec_cxt.periodic.rate: 200] 4997 5022 4977 5010 4994 4998 4992 5013 4993 5004 5015 4979 5006 4996 4990 5009 4999 4998 5007 4998 4984
이와 같이, ARTLinux를 이용해 기존의 컴포넌트를 리얼타임화할 수 있습니다.
OpenRTM-aist 및 RT컴포넌트를 Intel(i386) 이외의 CPU로 실행하고 싶은 경우, SH4나 PPC 등 느린 CPU에서는 컴파일에 시간이 걸려 효율적으로 개발할 수 없는 경우가 있습니다. 다른 CPU용의 OpenRTM 및 RT컴포넌트를 Intel(i386) CPU상에서 컴파일·개발하기 위한 크로스 개발 환경을 작성하면, C2D나 Quad 등 최근이 빠른 PC상에서 효율적으로 개발할 수 있습니다. 이 TIPS는 アックス의 森田씨로부터 제공되었습니다.
크로스 개발에 필요한 파일을 다운로드합니다.
$./Build_SH4_SDK.sh [(B)의 전체 경로] [(A)의 전체 경로] [(C)의 전체 경로]
스크립트의 실행이 완료되면 디렉토리(B) 내에 OpenRTM의 환경과 SH4의 toolchain, 빌드 된 ACE 라이브러리가 작성됩니다.
void registerPort(CorbaPort& port); bool addPort(PortBase& port); bool addPort(PortService_ptr port); bool addPort(CorbaPort& port); bool addInPort(const char* name, InPortBase& inport); bool addOutPort(const char* name, OutPortBase& outport);
포트의 등록: 기존의 registerPort등의 멤버 함수와의 차이는, 등록의 성공 여부를 |
반환값으로 돌려주는지 아닌지이다. 등록이 실패하는 것은 이미 같은 포트명이나 같은 포트의 인스턴스가 등록되어 있었을 경우이다. 그 이외의 기능은 같다.
bool removePort(PortBase& port); bool removePort(PortService_ptr port); bool removeInPort(InPortBase& port); bool removeOutPort(OutPortBase& port); bool removePort(PortBase& port); bool removePort(PortService_ptr port); bool removePort(CorbaPort& port);
포트의 등록 해제: 기존의 deletePort등의 멤버 함수와의 차이는, 등록의 성공 여부를 |
반환값으로 돌려주는지 아닌지이다. 등록이 실패하는 것은 이미 같은 포트명이나 같은 포트의 인스턴스가 등록되어 있었을 경우이다. 그 이외의 기능은 같다.
bool readAll();
class DataIn: public RTC::DataFlowComponentBase { RTC::TimedLong input_data1_; RTC::InPort<RTC::TimedLong> inport1_; RTC::TimedLong input_data2_; RTC::InPort<RTC::TimedLong> inport2_; public: explicit DataIn(RTC::Manager* manager): RTC::DataFlowComponentBase(manager), input_data1_(), inport1_("inport1", input_data1_), input_data2_(), inport2_("inport2", input_data2_) { } virtual RTC::ReturnCode_t onInitialize() { registerInPort("inport1", inport1_); registerInPort("inport2", inport2_); return RTC::RTC_OK; } virtual RTC::ReturnCode_t onExecute(RTC::UniqueId exec_handle) { // inport1_과 inport2_에 대해서 read를 호출한다 const bool is_read_success = readAll(); if (is_read_success) { std::cout << input_data1_.data << std::endl; std::cout << input_data2_.data << std::endl; } return RTC::RTC_OK; } }; bool writeAll();
class DataOut: public RTC::DataFlowComponentBase { RTC::TimedLong output_data1_; RTC::OutPort<RTC::TimedLong> outport1_; RTC::TimedLong output_data2_; RTC::OutPort<RTC::TimedLong> outport2_; public: explicit DataOut(RTC::Manager* manager): RTC::DataFlowComponentBase(manager), output_data1_(), outport1_("outport1", output_data1_), output_data2_(), outport2_("outport2", output_data2_) { } virtual RTC::ReturnCode_t onInitialize() { registerOutPort("outport1", outport1_); registerOutPort("outport2", outport2_); return RTC::RTC_OK; } virtual RTC::ReturnCode_t onExecute(RTC::UniqueId exec_handle) { output_data1_.data += 2; output_data2_.data += 3; // outport1_과 outport2_에 대해서 write를 호출한다 const bool is_write_success = writeAll(); if (!is_write_success) { std::cout << "write fail" << std::endl; } return RTC::RTC_OK; } }; void setReadAll(bool read=true, bool completion=false);
class DataIn: public RTC::DataFlowComponentBase { RTC::TimedLong input_data1_; RTC::InPort<RTC::TimedLong> inport1_; RTC::TimedLong input_data2_; RTC::InPort<RTC::TimedLong> inport2_; public: explicit DataIn(RTC::Manager* manager): RTC::DataFlowComponentBase(manager), input_data1_(), inport1_("inport1", input_data1_), input_data2_(), inport2_("inport2", input_data2_) { } virtual RTC::ReturnCode_t onInitialize() { registerInPort("inport1", inport1_); registerInPort("inport2", inport2_); setReadAll(); // ★ return RTC::RTC_OK; } virtual RTC::ReturnCode_t onExecute(RTC::UniqueId exec_handle) { // onExecute가 불리기 직전에 inport1_과 inport2_의 read가 불리고 있다 std::cout << input_data1_.data << std::endl; std::cout << input_data2_.data << std::endl; return RTC::RTC_OK; } };
void setWriteAll(bool write=true, bool completion=false);
class DataOut: public RTC::DataFlowComponentBase { RTC::TimedLong output_data1_; RTC::OutPort<RTC::TimedLong> outport1_; RTC::TimedLong output_data2_; RTC::OutPort<RTC::TimedLong> outport2_; public: explicit DataOut(RTC::Manager* manager): RTC::DataFlowComponentBase(manager), output_data1_(), outport1_("outport1", output_data1_), output_data2_(), outport2_("outport2", output_data2_) { } virtual RTC::ReturnCode_t onInitialize() { registerOutPort("outport1", outport1_); registerOutPort("outport2", outport2_); setWriteAll(); // ★ return RTC::RTC_OK; } virtual RTC::ReturnCode_t onExecute(RTC::UniqueId exec_handle) { output_data1_.data += 2; output_data2_.data += 3; return RTC::RTC_OK; // onExecute를 빠진 직후에 outport1_과 outport2_의 write가 불린다 } };
class MyDataListener : public ConnectorDataListenerT<RTC::TimedLong> { public: virtual void operator()(const ConnectorInfo& info, const TimedLong& data) { // 콜백의 처리 }; };
MyDataListener *listner = new MyDataListener(); m_port.addConnectorDataListener(ON_BUFFER_WRITE, listner);
m_port.removeConnectorDataListener(ON_BUFFER_WRITE, listner);
class MyConnectorListener : public ConnectorListener { public: virtual void operator()(const ConnectorInfo& info) { // 콜백의 처리 }; };
MyConnectorListener *listner = new MyConnectorListener(); m_port.addConnectorListener(ON_BUFFER_EMPTY, listner);
m_port.removeConnectorListener(ON_BUFFER_EMPTY, listner);
void setOnPublishInterfaces(ConnectionCallback* on_publish);
void setOnSubscribeInterfaces(ConnectionCallback* on_subscribe);
void setOnConnected(ConnectionCallback* on_connected);
뮤텍스가 락 되고 있기 때문에, 콜백내에서 콜백을 설정한 포트에 대해서 get_connector_profiles나 get_port_profile 등을 호출하면 데드 락 되는 것에 주의. 콜백을 설정한 포트 이외의 포트에 대해서는 뮤텍스가 락 되어 있지 않기 때문에, get_port_profile등의 호출은 가능. 예를 들면, 포트1으로 포트2를 접속하는 것을 생각한다. 포트1에 setOnConnected로 콜백을 설정해 둔다. 이 콜백내로부터 포트1에 대해서 get_port_profile등의 호출을 실시하면 데드 락 된다. 포트2에 대해서는 get_port_profile등을 호출할 수 있다.
void setOnUnsubscribeInterfaces(ConnectionCallback* on_subscribe);
뮤텍스가 락 되고 있기 때문에, 콜백내에서 접속 대상 포트의 get_connector_profiles나 get_port_profile 등을 호출하면 데드 락 되는 것에 주의. setOnConnected와 달라, 콜백을 설정하고 있지 않는 포트에 대해도 get_port_profile 등을 호출할 수 없는 것에 주의. 예를 들면, 포트1으로 포트2를 접속하는 것을 생각한다. 포트1에 setOnConnected로 콜백을 설정해 둔다. 이 콜백내에서는 포트1, 포트2 모두 get_port_profile등의 호출을 실시하면 데드 록 된다.
void setOnDisconnected(ConnectionCallback* on_disconnected);
절단 처리의 도중에 어떠한 에러가 있어도 콜백은 불려 가는 것에 주의. 또, 뮤텍스의 락 되고 있기 때문에, setOnUnsubscribeInterfaces와 같이, 데드 락에 주의.
void setOnConnectionLost(ConnectionCallback* on_connection_lost);
다만, 이 콜백에 관해서는, PortBase에서는 처리의 구현을 하고 있지 않고, 상속 클래스측에서 구현하게 되어 있다. 현재는, OutPort에만 구현되고 있어 InPort와 CorbaPort에서는 콜백을 설정해도 불려 갈 것은 없다.
다만, addPort나 registerPort등에서 포트를 RTC에 등록하고 있지 않는 상태에서는,.[포트명]이 되어 있다. 또, addPort나 registerPort등의 호출을 onInitialize 이전(constructor등)에 실시하면, RTC의 인스턴스명이 확정하고 있지 않기 때문에, 포트명은 .[포트명]이 되어 버린다.
subscribeInterfaces의 호출에 대해서, 도중에 1개에서도 실패했을 경우, 즉석에서 에러 복귀한다
subscribeInterfaces의 호출에 대해서, 도중에 실패해도, 그대로 처리는 속행한다. 접속 대상 포트 모두에 대해서 처리를 한 후, 발생한 에러에 대응하는 에러 반환값을 돌려준다.
const char* getName() const;
·PortBase의 protected 멤버 함수 eraseConnectorProfile가
뮤텍스의 락을 실시하지 않게 되었다.
·PortBase의 이하의 멤버 함수를 호출했을 때,
접속하고 있는 포트를 모두 체크해 무효가 된 포트가 있으면, 절단 처리를 실시하게 되었다. get_port_profile() get_connector_profiles() get_connector_profile()
·포트의 프호퍼티"connection_limit"를 지정하면,
접속수의 제한을 할 수 있게 되었다.
■데이터 포트 【콜백 관련】 ·InPort의 이하의 콜백 설정 함수가 삭제되었다.
그 대신해, addConnectorDataListener를 사용한다. void setOnWrite(OnWrite<DataType>* on_write); void setOnWriteConvert(OnWriteConvert<DataType>* on_wconvert); void setOnOverflow(OnOverflow<DataType>* on_overflow); void setOnUnderflow(OnUnderflow<DataType>* on_underflow);
·OutPot의 이하의 멤버 함수가 삭제되었다.
콜백의 설정은 addConnectorDataListener를 사용한다. bool read(DataType& value); void setReadBlock(bool block); void setWriteBlock(bool block); void setReadTimeout(long int timeout); void setWriteTimeout(long int timeout); void setOnOverflow(OnOverflow<DataType>* on_overflow); void setOnRead(OnRead<DataType>* on_read); void setOnReadConvert(OnReadConvert<DataType>* on_rconvert); void setOnUnderflow(OnUnderflow<DataType>* on_underflow); void setOnConnect(OnConnect* on_connect); void setOnDisconnect(OnConnect* on_disconnect); void onConnect(const char* id, PublisherBase* publisher); void onDisconnect(const char* id);
【접속시의 정책 설정】 ·RT System Editor로 데이터 포트를 접속할 때,
송신 정책과 버퍼링 정책을 지정할 수 있게 되었다.
송신 정책(접속 방식이 flush(디폴트)의 경우는 사용할 수 없다) - All: InPort의 버퍼에 쌓여 있는 데이터를 한 번에 모두 송신한다 - Fifo: InPort의 버퍼에 쌓여 있는 데이터를 FIFO로 간주해(오래된 것부터 1개씩) 송신한다 - Skip: InPort의 버퍼에 쌓여 있는 데이터를 스킵해 송신한다 - New: InPort의 버퍼에 쌓여 있는 데이터의 최신값만을 송신한다
버퍼링 정책 ·버퍼 기입시에 버퍼가 풀이었던 경우의 동작을 지정할 수 있다. - overwrite: 덮어쓰기한다 - do_nothing: 아무것도 하지 않는다 - block: 블록 한다. 타임 아웃 설정가능.
·버퍼 읽기시에 버퍼가 빈 경우의 동작을 지정할 수 있다. - readback: 최신의 값을 다시 읽는다 - do_nothing: 아무것도 하지 않는다 - block: 블록 한다. 타임 아웃 설정가능.
·데이터 포트의 접속시의 프로퍼티에"dataport.serializer.cdr.endian"
을 지정하면, 송수신 하는 데이터의 endian를 변경할 수 있다. 그러나, RT System Editor는 대응하고 있지 않다.
【그 외】 ·DataPortStatus에 BUFFER_ERROR가 추가되었다.
·데이터 포트로 CORBA 대신에 TCP/IP를 이용하고 통신을 실시하는 것이
할 수 없게 되었다. (TCP/IP용의 Provider/Consumer를 자작하면 가능하다고 생각된다)
·InPort::read의 반환값의 형태가 변경되었다.
(RC1) 읽어들인 데이터 (RELEASE) 읽을 성공 여부를 나타내는 bool
·Timed*의 데이터에 대해서 현재 시각을 부여하는 함수 setTimestamp()가 추가되었다.
·OutPort::write()로 타임 스탬프를 부여하지 않게 되었다.
·OutPot에 이하의 멤버 함수가 추가되었다.
이것을 이용하면 1대 다접속 시에 접속마다의 write의 결과를 취득할 수 있다. DataPortStatus::Enum getStatus(int index); DataPortStatusList getStatusList();
·데이터 포트의 pull의 동작
- InPort::isNew()는 항상 false를 돌려준다. - 송신측의 버퍼에 데이터가 없는 경우는, InPort::read()의 반환값이 false가 된다. - OutPort측의 Buffer empty policy의 설정에 의해, 버퍼가 빈 경우의 동작이 다르다. 디폴트로는 readback이 되어 있기 때문에, 한 번 데이터를 write() 하는과 같은 값을 계속읽는다.
데이터 포트를 push로 접속했을 경우와 pull로 접속했을 경우에서는 프로그래밍 스타일이 다르다.
[push의 경우] RTC::ReturnCode_t ConsoleOut::onExecute(RTC::UniqueId ec_id) { if (m_inport.isNew()) { m_inport.read(); // 데이터를 이용한 처리 } return RTC::RTC_OK; }
[pull의 경우] RTC::ReturnCode_t ConsoleOut::onExecute(RTC::UniqueId ec_id) { bool ret = m_inport.read(); if (ret == true) { // 데이터를 이용한 처리 } return RTC::RTC_OK; }
■서비스 포트 【접속시의 정책 설정】 ·CorbaPort의 접속에 대해 ConnectorProfile::properties에 "port.connection.strictness"라고 하는 프로퍼티를 설정할 수 있게 되었다. 이 프로퍼티에는 "strict" 나 "best_effort"를 설정한다.
CorbaPort.h에서 인용 * strict: 모든 컨슈머(consumer)로 지정한 참조가 존재해, 한편 네로잉에도 * 성공해 컨슈머(consumer)에게 적절히 세트 할 수 있었을 경우에게만 Port * 사이의 접속을 확립한다. * * best_effort: 네로잉등에 실패했을 경우에서도, 에러를 돌려주는 것 * 구 Port 간의 접속을 확립한다.
【그 외】 ·CorbaPort로, 접속중에 RTC를 Activate, Deactivate 해, 재차 Activate 하면 컨슈머(consumer)측으로부터 액세스 할 수 없는 문제가 해결되었다.
■Manager 관계 Manager에 마스터와 슬레이브라고 하는 관계를 할 수 있었다. 자세한 것은[openrtm-users 01099]의 安藤씨의 해설을 참조.
【rtc.conf에게 주는 프로퍼티의 변경】 ·Manager의 프로퍼티(rtc.conf에게 주는 프로퍼티)로 변경이 있었다.
"corba.endpoint" 사용할 수 없게 되었다.대신에"corba.endpoints"를 사용하게 되었다.
"manager.is_master" NO로 하면 Manager의 CORBA 오브젝트를 네이밍 서비스에 등록하지 않게 되었다.
"manager.corba_servant" NO로 하면 Manager의 CORBA 오브젝트를 생성하지 않는다.
"manager.is_master" YES이면, Manager의 프로퍼티"corba.master_manager"로 지정되어 있는 포트 번호를 ORB의 엔드 포인트로서 추가하게 되었다.
"manager.shutdown_on_nortcs" YES의 경우,"manager.is_master"NO이면, 프로세스내의 RTC가 0개에 된 타이밍에 Manager가 자동 종료하게 되었다. 다만,[openrtm-users 01149]의 패치를 대는 것.
【RTC 기동시의 커멘드 라인 인수의 옵션의 추가】
-a 지정하면, Manager의 CORBA 오브젝트를 생성하지 않는다. 다만, ManagerConfig::parseArgs()에 버그가 있기 위해, 이 옵션을 지정해도 Manager의 CORBA 오브젝트의 생성이 유효하게 된다.("maanger.corba_servant"로 스펠 미스) SVN의 최신 소스에서는 수정되고 있는 모양. -d 지정하면, Manager가 마스터가 된다. -o Manager의 프로퍼티를 추가할 수 있다. 이 옵션에 이어 (프로퍼티의 키):(프로퍼티의 값)라고 쓴다. 예를 들면, -o test.property:100 -p Manager의 프로퍼티"corba.endpoints"에 포트 번호를 추가한다. 예를 들면, -p 50000
【Manager의 내부 구현의 변경】 ·RC1에서는 미구현이었던 ModuleManager::getLoadableModules()가 구현되었다.
→ Manager::getLoadableModules()를 사용할 수 있게 되었다.
·Manager::setModuleInitProc()로 설정한 함수가 실행된다
타이밍이 변경되었다. Manager::activateManager()로 실행되는 것 자체는 변경되어 있지 않다. 그러나, Manager의 프로퍼티"manager.components.precreate"로 지정된 RTC를 기동하는 처리와의 차례가 변경되고 있다. (RC1) "manager.components.precreate"의 RTC를 생성한 후 (RELEASE) "manager.components.precreate"의 RTC를 생성하기 전
·Manager에 RTC의 가비지 콜렉션과 같은 기능이 추가되었다.
RTC를 exit 하면, Manager가 1초 주기에 exit 된 RTC를 delete 한다. 다만, 버그 있어.[openrtm-users 01149]의 패치를 대는 것. 또, createComponent로 생성한 RTC가 on_initialize로 실패하면 그 RTC는 delete 되지 않고, 메모리 리크 한다. RTC의 initialize가 실패하면 RTC를 exit 하지만, exit는 initialize가 성공하지 않았다고 처리를 거부하기 위해, Manager::notifyFinalized가 불리지 않고, Manager의 RTC 가비지 콜렉션이 일하지 않기 때문에.
·Manager가 RTC를 삭제할 때, delete를 직접 사용하는 것이 아니라,
Manager::registerFactory로 등록된 RTC 삭제 함수를 사용하도록 수정되었다.
·Manager::load()로, 제2 인수의 초기화 함수명이 빈 경우의 처리가 추가되었다.
초기화 함수명이 빈 경우는, 제1 인수의 모듈명에"Init"를 부여했다 초기화 함수명으로 모듈의 로드를 시도하게 되었다.
·Manager::createComponent로, 지정한 RTC를 생성하는 Factory가
등록되지 않은 경우, Manager의 프로퍼티"manager.modules.load_path"로 지정한 패스로부터, 지정한 RTC를 생성 가능한 모듈을 검색해, 자동 로드하는 기능이 추가되었다.
【IDL 인터페이스의 변경】 ·IDL 인터페이스 RTM::Manager가 변경되었다.
(추가) boolean is_master(); ManagerList get_master_managers(); RTC::ReturnCode_t add_master_manager(in Manager mgr); RTC::ReturnCode_t remove_master_manager(in Manager mgr); ManagerList get_slave_managers(); RTC::ReturnCode_t add_slave_manager(in Manager mgr); RTC::ReturnCode_t remove_slave_manager(in Manager mgr); (삭제) Manager get_owner(); Manager set_owner(in Manager mgr); Manager get_child(); Manager set_child(in Manager mgr);
·ManagerServant::get_loadable_modules(),
ManagerServant::get_loaded_modules(), ManagerServant::get_factory_profiles()는, 본래, 자신의 것 뿐만이 아니라, 슬레이브의 모듈도 취득할 수 있는 메소드같지만, 현재의 실장에서는 자신의 모듈 밖에 취득할 수 없다.