'This is a work in progress''
This part builds a transport plugin allowing Orocos to communicate these types across CORBA.
In a shell
cd /path/to/plugins mkdir build cd build cmake .. -DOROCOS_TARGET=macosx -DENABLE_CORBA=ON make
The only difference from building in Part 1, is to turn ON CORBA.
For other operating systems substitute the appopriate value for "macosx" when setting OROCOS_TARGET (e.g. "gnulinux").
Tested in Mac OS X Leopard 10.5.7.
In a shell
cd /path/to/plugins/build/corba/tests ./corba-recv
In a second shell
cd /path/to/plugins/build/corba/tests ./corba-send
Now the same exact two test components of Parts 1 and 2 are in separate processes. Typing ls in either process will present the same values (subject to network latency delays, which typically are not human perceptible) - the data and types are now being communicated between deployers.
Now, the transport plugin is responsible for communicating the types between deployers, while the toolkit plugin is responsible for knowing each type and being able to display it. Separate responsibilities. Separate plugins.
NB for the example components, send must be started after recv. Starting only corba-recv and issuing ls will display the default values for each type. Also, quitting the send component and then attempting to use the recv component will lockup the recv deployer. These limitations are not due to the plugins - they are simply due to the limited functionality of these test cases.
Running the same two corba test programs but without loading the transport plugin, is instructive as to what happens when you do not match up certain things in the toolkit sources. This is very important!
In a shell
cd /path/to/plugins/build/corba/tests ./corba-recv-no-toolkit
Data Flow Ports: RW(U) boost_ptime ptime = not-a-date-time RW(U) boost_timeduration timeDuration = 00:00:00
In a second shell
cd /path/to/plugins/build/corba/tests ./corba-send-no-toolkit
The send component without the transport plugin fails to start, with:
$ ./build/corba/tests/corba-send-no-toolkit 0.008 [ Warning][./build/corba/tests/corba-send-no-toolkit::main()] Forcing priority (0) of thread to 0. 0.008 [ Warning][PeriodicThread] Forcing priority (0) of thread to 0. 0.027 [ Warning][SingleThread] Forcing priority (0) of thread to 0. 5.078 [ Warning][./build/corba/tests/corba-send-no-toolkit::main()] ControlTask 'Send' already bound \ to CORBA Naming Service. 5.078 [ Warning][./build/corba/tests/corba-send-no-toolkit::main()] Trying to rebind... done. New \ ControlTask bound to Naming Service. 5.130 [ Warning][./build/corba/tests/corba-send-no-toolkit::main()] Can not create a proxy for data \ connection. 5.130 [ ERROR ][./build/corba/tests/corba-send-no-toolkit::main()] Dynamic cast failed \ for 'PN3RTT14DataSourceBaseE', 'unknown_t', 'unknown_t'. Do your typenames not match? Assertion failed: (doi && "Dynamic cast failed! See log file for details."), function createConnection, \ file /opt/install/include/rtt/DataPort.hpp, line 462. Abort trap
*** corba/tests/corba-recv.cpp 2009-07-29 22:08:32.000000000 -0400 --- corba/tests/corba-recv-no-toolkit.cpp 2009-08-09 16:32:03.000000000 -0400 *************** *** 11,17 **** #include <rtt/os/main.h> #include <rtt/Ports.hpp> - #include "../BoostCorbaToolkit.hpp" #include "../../BoostToolkit.hpp" // use Boost RTT Toolkit test components --- 11,16 ---- *************** *** 27,33 **** int ORO_main(int argc, char* argv[]) { RTT::Toolkit::Import( Examples::BoostToolkit ); - RTT::Toolkit::Import( Examples::Corba::corbaBoostPlugin ); Recv recv("Recv"); PeriodicActivity recv_activity( --- 26,31 ----
We define the CORBA types in corba/BoostTypes.idl. This is a file in CORBA's Interface Description Language (IDL). There are plenty of references on the web, for instance [1].
// must be in RTT namespace to match some rtt/corba code module RTT { module Corba {
struct time_duration { short hours; short minutes; short seconds; long nanoseconds; };
// can't get at underlying type, so send this way (yes, more overhead) // see BoostCorbaConversion.hpp::struct AnyConversion<boost::posix_time::ptime> // for further details. struct ptime { // julian day long date; time_duration time_of_day; }; }; };
Note that CORBA IDL knows about certain types already, e.g. short and long, and that we can use our time_duration structure in later structures.
We will come back to this IDL file during the build process.
The actual plugin is defined in corba/BoostCorbaToolkit.hpp. This is the equivalent of the BoostToolkit.hpp file, except for a transport plugin.
namespace Examples { namespace Corba { class CorbaBoostPlugin : public RTT::TransportPlugin { public: /// register this transport into the RTT type system bool registerTransport(std::string name, RTT::TypeInfo* ti); /// return the name of this transport type (ie "CORBA") std::string getTransportName() const; /// return the name of this transport std::string getName() const; }; // the global instance extern CorbaBoostPlugin corbaBoostPlugin; // namespace } }
The implementation of the plugin is in corba/BoostCorbaToolkit.cpp, and is very straight forward.
namespace Examples { namespace Corba { bool CorbaBoostPlugin::registerTransport(std::string name, TypeInfo* ti) { assert( name == ti->getTypeName() ); // name must match that in plugin::loadTypes() and // typeInfo::composeTypeInfo(), etc if ( name == "boost_ptime" ) return ti->addProtocol(ORO_CORBA_PROTOCOL_ID, new CorbaTemplateProtocol< boost::posix_time::ptime >() ); if ( name == "boost_timeduration" ) return ti->addProtocol(ORO_CORBA_PROTOCOL_ID, new CorbaTemplateProtocol< boost::posix_time::time_duration >() ); return false; }
std::string CorbaBoostPlugin::getTransportName() const { return "CORBA"; } std::string CorbaBoostPlugin::getName() const { return "CorbaBoost"; }
For a CORBA transport plugin, the name returned by getTransportName() should be CORBA.
CorbaBoostPlugin corbaBoostPlugin; // namespace } } ORO_TOOLKIT_PLUGIN(Examples::Corba::corbaBoostPlugin);
I will only cover the code for converting one of the types. The other is very similar - you can examine it yourself in the source file.
#include "BoostTypesC.h" #include <rtt/corba/CorbaConversion.hpp> #include <boost/date_time/posix_time/posix_time_types.hpp> // no I/O
// must be in RTT namespace to match some rtt/corba code namespace RTT {
template<> struct AnyConversion< boost::posix_time::time_duration > { // define the Corba and standard (ie non-Corba) types we are using typedef Corba::time_duration CorbaType; typedef boost::posix_time::time_duration StdType;
The last four of the following six functions are required by the CORBA library, to enable conversion between the CORBA and non-CORBA types. The two convert functions are their for convenience, and to save replicating code.
// convert CorbaType to StdTypes static void convert(const CorbaType& orig, StdType& ret) { ret = boost::posix_time::time_duration(orig.hours, orig.minutes, orig.seconds, orig.nanoseconds); } // convert StdType to CorbaTypes static void convert(const StdType& orig, CorbaType& ret) { ret.hours = orig.hours(); ret.minutes = orig.minutes(); ret.seconds = orig.seconds(); ret.nanoseconds = orig.fractional_seconds(); }
static CorbaType* toAny(const StdType& orig) { CorbaType* ret = new CorbaType(); convert(orig, *ret); return ret; } static StdType get(const CorbaType* orig) { StdType ret; convert(*orig, ret); return ret; } static bool update(const CORBA::Any& any, StdType& ret) { CorbaType* orig; if ( any >>= orig ) { convert(*orig, ret); return true; } return false; } static CORBA::Any_ptr createAny( const StdType& t ) { CORBA::Any_ptr ret = new CORBA::Any(); *ret <<= toAny( t ); return ret; } };
The same six functions then follow for our boost::ptime type. They are not covered in detail here.
IF (ENABLE_CORBA) INCLUDE(${CMAKE_SOURCE_DIR}/config/UseCorba.cmake)
FILE( GLOB IDLS [^.]*.idl ) FILE( GLOB CPPS [^.]*.cpp ) ORO_ADD_CORBA_SERVERS(CPPS HPPS ${IDLS} )
INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_BINARY_DIR}/. )
CREATE_COMPONENT(BoostToolkit-corba-${OROCOS_TARGET} VERSION 1.0.0 ${CPPS}) TARGET_LINK_LIBRARIES(BoostToolkit-corba-${OROCOS_TARGET} ${OROCOS-RTT_CORBA_LIBRARIES} ${CORBA_LIBRARIES})
SUBDIRS(tests) ENDIF (ENABLE_CORBA)
The corba-send test program instantiates a send component, and uses an RTT ControlTaskProxy to represent the remote receive component.
#include <rtt/corba/ControlTaskServer.hpp> #include <rtt/corba/ControlTaskProxy.hpp> #include <rtt/RTT.hpp> #include <rtt/PeriodicActivity.hpp> #include <rtt/TaskContext.hpp> #include <rtt/os/main.h> #include <rtt/Ports.hpp> #include <ocl/TaskBrowser.hpp> #include "../BoostCorbaToolkit.hpp" #include "../../BoostToolkit.hpp" #include "../../tests/send.hpp" using namespace std; using namespace Orocos; using namespace RTT::Corba; int ORO_main(int argc, char* argv[]) { RTT::Toolkit::Import( Examples::BoostToolkit ); RTT::Toolkit::Import( Examples::Corba::corbaBoostPlugin );
Send send("Send"); PeriodicActivity send_activity( ORO_SCHED_OTHER, 0, 1.0 / 10, send.engine()); // 10 Hz // start Corba and find the remote task ControlTaskProxy::InitOrb(argc, argv); ControlTaskServer::ThreadOrb();
TaskContext* recv = ControlTaskProxy::Create( "Recv" ); assert(NULL != recv);
if ( connectPeers( recv, &send ) == false ) { log(Error) << "Could not connect peers !"<<endlog(); } // create data object at recv's side if ( connectPorts( recv, &send) == false ) { log(Error) << "Could not connect ports !"<<endlog(); }
send.configure(); send_activity.start(); log(Info) << "Starting task browser" << endlog(); OCL::TaskBrowser tb( recv ); tb.loop(); send_activity.stop();
ControlTaskProxy::DestroyOrb(); return 0; }
The receive test program has a similar structure to the send test program.
#include <rtt/corba/ControlTaskServer.hpp> #include <rtt/corba/ControlTaskProxy.hpp> #include <rtt/RTT.hpp> #include <rtt/PeriodicActivity.hpp> #include <rtt/TaskContext.hpp> #include <rtt/os/main.h> #include <rtt/Ports.hpp> #include "../BoostCorbaToolkit.hpp" #include "../../BoostToolkit.hpp" #include "../../tests/recv.hpp" #include <ocl/TaskBrowser.hpp> using namespace std; using namespace Orocos; using namespace RTT::Corba; int ORO_main(int argc, char* argv[]) { RTT::Toolkit::Import( Examples::BoostToolkit ); RTT::Toolkit::Import( Examples::Corba::corbaBoostPlugin ); Recv recv("Recv"); PeriodicActivity recv_activity( ORO_SCHED_OTHER, 0, 1.0 / 5, recv.engine()); // 5 Hz // Setup Corba and Export: ControlTaskServer::InitOrb(argc, argv); ControlTaskServer::Create( &recv ); ControlTaskServer::ThreadOrb();
// Wait for requests: recv.configure(); recv_activity.start(); OCL::TaskBrowser tb( &recv ); tb.loop(); recv_activity.stop();
// Cleanup Corba: ControlTaskServer::ShutdownOrb(); ControlTaskServer::DestroyOrb(); return 0; }
The no-toolkit versions of the test programs are identical, except they simply do not load the transport plugin, making it impossible to transport the boost types over CORBA.