Component overlays

A component overlay is a special pseudo component (sensor or actuator) that overrides the default component behaviour to better fit into a particular architecture.

Warning

As of MORSE 1.0, only services can be overridden. In future versions, the stream-oriented interfaces of components (like ROS topics or YARP port) may be overridable as well.

Overlay example

The example below present a typical use for overlays: MORSE provides a pan-tilt unit actuator that offers a method, morse.actuators.ptu.PTU.set_pan_tilt(), to set the pan and tilt angles of the PTU.

But in your architecture, you are using 2 different methods, SetTilt and SetPan.

The following overlay maps your functions to the default MORSE ones:

from morse.core.services import service
from morse.core.overlay import MorseOverlay
from morse.core import status

class MyPTU(MorseOverlay):

    def __init__(self, overlaid_object):
        # Call the constructor of the parent class
        MorseOverlay.__init__(self, overlaid_object)

        self.last_tilt = 0.0
        self.last_pan = 0.0

    @service
    def SetTilt(self, tilt):

        self.last_tilt = float(tilt)
        self.overlaid_object.set_tilt_pan(self.last_tilt, self.last_pan)

    @service
    def SetPan(self, pan):

        self.last_pan = float(pan)
        self.overlaid_object.set_tilt_pan(self.last_tilt, self.last_pan)

You can save this overlay anywhere, for instance in a morse.my_overlays.py file, accessible from MORSE Python path.

For asynchronous service, it is a bit more complex, as we need to provide a callback. The morse.core.overlay.MorseOverlay.chain_callback() takes care about this operation. You can pass an optional function to this method to modify the returned data, or modify the state of your object.

This new callback must take one parameter (a tuple (status, result)) and return a new tuple (status, result):

from morse.core.services import async_service
from morse.core.overlay import MorseOverlay
from morse.core import status

class MyPTU(MorseOverlay):

    def __init__(self, overlaid_object):
        # Call the constructor of the parent class
        MorseOverlay.__init__(self, overlaid_object)

        self.last_tilt = 0.0
        self.last_pan = 0.0

    def format_pan_tilt_return(self, result):
        # this callback will be called when SetTilt or SetPan
        # completes.
        # It simply re-format the return value of the asynchronous
        # services.

        status, value = result

        return (status,
                "PTU->{:.2f},{:.2f}".format(self.last_pan, self.last_tilt))

    @async_service
    def SetTilt(self, tilt):
        self.last_tilt = float(tilt)
        self.overlaid_object.set_tilt_pan(
                self.chain_callback(self.format_pan_tilt_return), \
                self.last_tilt, self.last_pan)

    @async_service
    def SetPan(self, pan):
        self.last_pan = float(pan)
        self.overlaid_object.set_tilt_pan(
                self.chain_callback(self.format_pan_tilt_return), \
                self.last_tilt, self.last_pan)

Warning

The behaviour is currently undefined in case of service name collision between the original sensor services and the services defined in the overlay.

Scene setup

With the MORSE Builder API

Components can be easily overlaid from the MORSE Builder API with the method morse.builder.abstractcomponent.AbstractComponent.add_overlay().

This method takes two parameters, the middleware to use (cf morse.builder.data for the list of available options) and the full-qualified Python name of the overlay class (for instance, morse.my_overlays.MyPTU)

The following example is taken from one of the ROS unit-tests:

#! /usr/bin/env morseexec

from morse.builder import *

robot = ATRV()

waypoint = Waypoint()
robot.append(waypoint)

waypoint.add_overlay('ros', 'morse.middleware.ros.overlays.waypoints.WayPoint')

env = Environment('indoors-1/indoor-1')

Here, the waypoint actuator get overlaid by the WayPoint class defined in the module morse.middleware.ros.overlays.waypoints.

Name remapping

Overlays also allow you to redefine the component name by overloading the morse.core.abstractobject.AbstractObject.name() method.

Let’s complete our previous example:

# [...]

class MyPTU(MorseOverlay):

    # [...]

    def name():
        return "MyPTU"

    # [...]

In this case, at initialization, a new (pseudo) component (called MyPTU in this case) is created, with services as defined in the overlay class.

The original component is also created and remains available as usual.

Table Of Contents

Previous topic

UTM

Next topic

Screencast

This Page