Service Port Communication


Service Port is a function which can be used much more freely like RPC (remote procedure call). Remote component can “call” another RTCs service port. The Remote can also get the result (return value) of the call.

On my personal opinion, service port is the double-edged sword.
Service Port is very powerful and able to be freely defined.
Therefore, beginners can define anything even if it is dirty, not smart function.

It is difficult to prepare universal debugging tools.
Data Port is very simple and easy to create those debugger (like data logger).

IDL

If you want to use Service Port, you need to have IDL (interface definition language) file that defines the service port’s RPC functions.
If you just use Service Port, you can get the IDL of the provider which develops the RTC you want to use.

How to define interface?

Grammer of IDL is similar to OOP language like Java, C++, and so on. Here is too small to explain whole grammers of IDL, so please search books or websites about IDL.

This is an example used here.
SimpleService

/**
 * Filename = SimpleService.idl
 * author: ysuga
 **/
module ysuga {

	interface SimpleService {
	  long read  (out long data);
	  long write (in  long data);
	  long reset ();
	};

};

Points are…

  1. module = package
  2. interface is interface. like virtual class in C++.
  3. write, read is operation = function (method)

direction

To define Operation, you need to give the direction of argument.
In the example, write has in-direction argument that means “call by value”.
read has out-direction argument that means “call by reference”.
IDL also has inout-direction. This can have its initial value.

Data type

Some CORBA’s common data type can be used like long, double, and so on.

RTC Builder

Use RTCB to generate skeleton code.

SimpleServiceProvier

Select “Service Port” tab, and “Add Port”.

“Name” is service port’s name. Position parameter is not important.

Then, “Add Interface” to the Port.
Select the interface you added.

Setting parameter is…

  1. Interface Name … SimpleService
  2. Direction … “Provided”
  3. Instance Name … simpleServiceProvider
  4. Var Name … simpleServiceProvider
  5. IDL file … IDL file. Use “Browse” button, and select the IDL you will use.
  6. Interface Type … You can use pulldown menu and select SimpleService
  7. IDL path … currently its not important.

You need to check following files:

  1. SimpleServiceProvider.h … definition of RTC
  2. SimpleServiceProvider.cpp … implementation of RTC
  3. SimpleServiceSVC_impl.h … definition of Service Port
  4. SimpleServiceSVC_impl.cpp … implementation of Service Port
Important files are:

  1. SimpleServiceProvider.java … definition of RTC
  2. SimpleServiceProviderComp.java … main function
  3. SimpleServiceProviderimpl.java … implementation of RTC
  4. SimpleServiceSVC_impl.java … implementation of Service Port
Important files are:

  1. SimpleServiceProvider.py … RTC
  2. SimpleService_idl_example.py … Service Port
  3. idlcompile.bat … Script for IDL compile

The implementation class for Service Port has “read” “write”, and “reset” functions that are defined in the IDL, so modify there functions.

If Consumer RTC calls functions, these implementations are called remotely.

The objects of this class is added automatically in the skeleton of RTC, so RTC can access the properties of service port in the functions like onExecute.

SimpleServiceConsumer

User of service port are called “consumer”. We can create skeleton code for RTCB.

Select “Service Port” tab, and “Add Port”.

Then, push “Add Interface” button.

Select the interface in the previous step.

Setting parameters are as follows:

  1. Interface Name … This value should be the same as Provider. SimpleService
  2. Direction … For consumer, “Required”
  3. Instance Name … instance name of service port class object. “simpleServiceConsumer”
  4. Var Name … name of variable of service port. “simpleServiceConsumer”
  5. IDL file … IDL file. use “Browse” button to select the idl you downloaded before.
  6. Interface Type … Select Interface name with Pull down menu.
  7. IDL path … You do not have to use this.

Consumer side is not particularly different from ordinal RTC project.
In build process, IDL compiler will generate Stub class which will help program to remotely call the services, so you can use service port as consumer.

Provider side

Service Port class

Add “data” member parameter, and write / read it.

Modify SimpleServiceProvider_impl.h, and add “m_data” member to SimpleServiceSVC_impl class.

public:
  /*!
   * @brief standard constructor
   */
   SimpleServiceSVC_impl();
  /*!
   * @brief destructor
   */
   virtual ~SimpleServiceSVC_impl();
 
   // attributes and operations
   CORBA::Long read(CORBA::Long& data);
   CORBA::Long write(CORBA::Long data);
   CORBA::Long reset();
 
/** Add From Here **/
private:
	long m_data;
public:
	long getData() { return m_data; }
	void setData(long data) {m_data = data;}
};
In Java version, build_SimpleServiceProvider.xml is automatically generated. The xml file is a setting file for “ant” build. Please right click the file and launch “ant build” in Eclipse.
Then, please “refresh” the src folder then, you will be able to build the project without any errors.
If you had problem,
1. Build path is wrong. Please modify the project property, and set “Library” to “OpenRTM-aist.***.jar” and “common-cli.***.jar” which are installed in C:¥Program Files¥OpenRTM-aist/1.*/jar folder.

SimpleServiceSVC_impl.java is Service Port’s class Add “data” member to SimpleServiceSVC.

public class SimpleServiceSVC_impl extends SimpleServicePOA{
 
	private int data;
	final public int getData() {return data;}
	final public void setData(int d) {data = d;}
 
    public SimpleServiceSVC_impl() {
        // Please add extra constructor code here.
    }
In Python version, idlcompile.bat is automatically generated. This file will launch the idl compiler, so please launch it first.

If you had any problem (you can not find any files generated), please modify the bat file like..

C:\Python26\omniidl.exe -bpython SimpleService.idl

And then, launch the bat file.

Then, modify SimpleService_idl_example.py, which is the Service Port implementation.
This class’s object is already included in SimpleService.py.

Let’s add “data” member variable to SimpleService_i class.

 
class SimpleService_i (ysuga__POA.SimpleService):
    """
    \class SimpleService_i
    Example class implementing IDL interface ysuga.SimpleService
    """
 
    def __init__(self):
        """
        \brief standard constructor
        Initialise member variables here
        """
 
        self._data = 0
        pass
 
    def getData(self):
        return self._data
 
    def setData(self, data):
        self._data = data

read operation

In read operation, “data” is read.

CORBA::Long SimpleServiceSVC_impl::read(CORBA::Long& data)
{
  // Please insert your code here and remove the following warning pragma
#ifndef WIN32
  #warning "Code missing in function <CORBA::Long SimpleServiceSVC_impl::read(CORBA::Long& data)>"
#endif
	data = getData();
  return 0;
}
In Java, out direction argument is held by **Holder class.

    public int read(org.omg.CORBA.IntHolder data) {
        // Please insert your code here and remove the following warning pragma
        // TODO "Code missing in function <int read(org.omg.CORBA.IntHolder data)>"
    	data.value = this.data;
        return 0;
    }
In Python, CORBA.NO_IMPLEMENT error is raised in default. Please comment out, and add your codes.

    # long read(out long data)
    def read(self):
        # raise CORBA.NO_IMPLEMENT(0, CORBA.COMPLETED_NO)
        # *** Implement me
        # Must return: result, data
        return (0, self._data)

In python, the out direction argument is not listed in the function’s argument list.
Instead, the return value includes the out direction value as the list.
The first element of the list is return value, and the second element is out data.

write operation

“In” direction argument is quite simple.

 
CORBA::Long SimpleServiceSVC_impl::write(CORBA::Long data)
{
  // Please insert your code here and remove the following warning pragma
#ifndef WIN32
  #warning "Code missing in function <CORBA::Long SimpleServiceSVC_impl::write(CORBA::Long data)>"
#endif
	m_data = data;
  return 0;
}
    public int write(int data) {
        // Please insert your code here and remove the following warning pragma
        // TODO "Code missing in function <int write(int data)>"
    	this.data = data;
        return 0;
    }
    # long write(in long data)
    def write(self, data):
        # raise CORBA.NO_IMPLEMENT(0, CORBA.COMPLETED_NO)
        # *** Implement me
        # Must return: result
        self._data = data
        return 0

reset operation

In “reset”, data is changed to zero, and the return value is previous data value.

CORBA::Long SimpleServiceSVC_impl::reset()
{
  // Please insert your code here and remove the following warning pragma
#ifndef WIN32
  #warning "Code missing in function <CORBA::Long SimpleServiceSVC_impl::reset()>"
#endif
	long old_data = m_data;
	m_data = 0;
  return old_data;
}
    public int reset() {
        // Please insert your code here and remove the following warning pragma
        // TODO "Code missing in function <int reset()>"
    	int buf = data;
    	data = 0;
        return buf;
    }
    # long reset()
    def reset(self):
        # raise CORBA.NO_IMPLEMENT(0, CORBA.COMPLETED_NO)
        # *** Implement me
        # Must return: result
        buffer = self._data
        self._data = 0
        return buffer

Provider side RTC

SimpleService RTC has simpleServiceProvider which is the object of Service Port class. You can access the data member by getData function that you previously added.

RTC::ReturnCode_t SimpleServiceProvider::onExecute(RTC::UniqueId ec_id)
{
	std::cout << "data is " << m_simpleServiceProvider.getData() << std::endl;
  return RTC::RTC_OK;
}
Modify SimpleServiceProviderImpl.java.

    @Override
    protected ReturnCode_t onExecute(int ec_id) {
    	System.out.println("data is "  + m_simpleServiceProvider.getData());
        return super.onExecute(ec_id);
    }
	def onExecute(self, ec_id):
		"""
		The execution action that is invoked periodically
		former rtc_active_do()
		\param ec_id target ExecutionContext Id
		\return RTC::ReturnCode_t
		"""
 
		buf = self._simpleServiceProvider.getData()
		print ("data is %d" , buf)
		return RTC.RTC_OK

Consumer side RTC

In onExecute function, switch function call according to the key-input.
Service Port is added as simpleServiceConsumer.

RTC::ReturnCode_t SimpleServiceConsumer::onExecute(RTC::UniqueId ec_id)
{
	std::cout << "Input Command (q:reset, r:read, w:write)" << std::endl;
 
	char c;
	std::cin >> c;
 
	CORBA::Long data;
 
	switch(c) {
		case 'r':
			m_simpleServiceConsumer->read(data);
			std::cout << "read(): Data is " << (long)data << std::endl;
			break;
		case 'w':
			data = (long)rand();
			std::cout << "write(" << (long)data << ")" << std::endl;
			m_simpleServiceConsumer->write(data);
			break;
		case'q':
			data = m_simpleServiceConsumer->reset();
			std::cout << "reset(): return value is " << data << std::endl;
			break;
		default:
			break;
	}
 
	return RTC::RTC_OK;
}
	@Override
	protected ReturnCode_t onExecute(int ec_id) {
		System.out.println("Input Command: r:read, w:write, q:reset");
		try {
			int c = System.in.read();
 
			switch (c) {
			case 'r':
				IntHolder intHolder = new IntHolder();
				this.m_simpleServiceConsumerBase._ptr().read(intHolder);
				System.out.println("read(): data is " + intHolder.value);
				break;
			case 'w':
				int val = new Random().nextInt();
				this.m_simpleServiceConsumerBase._ptr().write(val);
				System.out.println("write(" + val + ")");
				break;
			case 'q':
				int ret = this.m_simpleServiceConsumerBase._ptr().reset();
				System.out.println("reset():returns " + ret);
				break;
			default:
				break;
			}
		} catch (Exception e) {
			System.out.println("Exception occurred:" + e);
		}
		return super.onExecute(ec_id);
	}
	def onExecute(self, ec_id):
		"""
 
		The execution action that is invoked periodically
		former rtc_active_do()
 
		\param ec_id target ExecutionContext Id
 
		\return RTC::ReturnCode_t
 
		"""
		data = random.randint(1, 10) # need to import random
 
		print "Input data"
		c = raw_input()
		if c == 'q':
			retval = self._simpleServiceConsumer._ptr().reset()
			print ("reset() returns " , retval)
		if c == 'r':
			retval = self._simpleServiceConsumer._ptr().read()
			print ("read() returns " , retval[0])
			print ("       data is " , retval[1])
		if c == 'w':
			retval = self._simpleServiceConsumer._ptr().write(data)
			print ("write(", data, ")" )
 
 
		return RTC.RTC_OK

rtc.conf

You can modify rtc.conf if you needed.

Execution

Launch Name Service first, then, execute Provider and Consumer.

Connect both Service ports and activate them all.

Select consumer’s window, and ‘w’ ‘r’ ‘q’ key will invoke the corresponding functions.

Summary

In my usual way, data transportation is done by Data Ports.
Reset operation can be realized by the state-changed event handler, so usually I do not use reset function.

So…

I hardly use Service Ports;-)