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 presents 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 two 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’s Python path.
For asynchronous services, it is a bit more complex, as we need to provide a
callback. The morse.core.overlay.MorseOverlay.chain_callback()
takes care
of this operation. You can pass an optional function to this method to
modify the returned data, or modify the state of your object.
If provided, the 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-formats 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 the case of a service name collision between the original sensor services and the services defined in the overlay.
Scene setup¶
With the MORSE Builder API¶
Components can easily be overlaid using the MORSE Builder API with the method
morse.builder.abstractcomponent.AbstractComponent.add_overlay()
.
This method takes two parameters, the middleware to use (see
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 has been 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(self):
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.