Source code for rocon_interactions.interactions

#
# License: BSD
#   https://raw.github.com/robotics-in-concert/rocon_tools/license/LICENSE
#
##############################################################################
# Description
##############################################################################

"""
.. module:: interactions
   :platform: Unix
   :synopsis: Representative class and methods for an *interaction*.


This module defines a class and methods that represent the core of what
an interaction is.

----

"""
##############################################################################
# Imports
##############################################################################

import rospkg
import rocon_console.console as console
import rocon_python_utils

from .exceptions import InvalidInteraction
from . import web_interactions
from . import utils

##############################################################################
# Classes
##############################################################################


[docs]class Interaction(object): ''' This class defines an interaction. It does so by wrapping the base rocon_interaction_msgs.Interaction_ msg structure with a few convenient variables and methods. .. include:: weblinks.rst ''' __slots__ = [ 'msg', # rocon_interaction_msgs.Interaction ]
[docs] def __init__(self, msg): """ Validate the incoming fields supplied by the interaction msg and populate remaining fields with proper defaults (e.g. calculate the unique hash for this interaction). The hash is calculated based on the incoming name-group-namespace triple. :param msg: underlying data structure with fields minimally filled via :func:`.load_msgs_from_yaml_resource`. :type msg: rocon_interaction_msgs.Interaction_ :raises: :exc:`.InvalidInteraction` if the interaction variables were improperly defined (e.g. max = -1) .. include:: weblinks.rst """ self.msg = msg """Underlying data structure (rocon_interaction_msgs.Interaction_)""" if self.msg.max < -1: raise InvalidInteraction("maximum instance configuration cannot be negative [%s]" % self.msg.name) if self.msg.max == 0: self.msg.max = 1 if self.msg.group == '': raise InvalidInteraction("group not configured [%s]" % self.msg.name) if self.msg.icon.resource_name == "": self.msg.icon.resource_name = 'rocon_bubble_icons/rocon.png' if not self.msg.icon.data: try: self.msg.icon = rocon_python_utils.ros.icon_resource_to_msg(self.msg.icon.resource_name) except rospkg.common.ResourceNotFound as unused_e: # replace with default icon if icon resource is not found. self.msg.icon.resource_name = 'rocon_bubble_icons/rocon.png' self.msg.icon = rocon_python_utils.ros.icon_resource_to_msg(self.msg.icon.resource_name) if self.msg.namespace == '': self.msg.namespace = '/' self.msg.hash = utils.generate_hash(self.msg.name, self.msg.group, self.msg.namespace)
# some convenient aliases - these should be properties!
[docs] def is_paired_type(self): """ Classify whether this interaction is to be paired with a rapp or not. :returns: whether it is a pairing interaction or not :rtype: bool """ return True if self.msg.required_pairings else False
############################################################################## # Conveniences ############################################################################## @property def command(self): """Executable name for this interaction, can be a roslaunch, rosrunnable, global executable, web url or web app [int].""" return self.msg.command @property def group(self): """The group under which this interaction should be embedded [int].""" return self.msg.group @property def compatibility(self): """A rocon_uri_ string that indicates what platforms it may run on [int].""" return self.msg.compatibility @property def name(self): """A human friendly name that also uniquely helps uniquely identify this interaction (you can have more than one configured ``command`` instance) [int].""" return self.msg.name @property def description(self): return self.msg.description @property def icon(self): return self.msg.icon @property def namespace(self): """Default namespace under which ros services and topics should be embedded for this interaction [int].""" return self.msg.namespace @property def max(self): """ Maximum number of instantiations that is permitted (e.g. teleop should only allow 1) [int]. """ return self.msg.max @property def remappings(self): return self.msg.remappings @property def parameters(self): return self.msg.parameters @property def hash(self): """A crc32 unique identifier key for this interaction, see also :func:`.generate_hash` [int32].""" return self.msg.hash @property def bringup_pairing(self): return self.msg.bringup_pairing @property def teardown_pairing(self): return self.msg.teardown_pairing @property def required_pairings(self): return self.msg.required_pairings def __eq__(self, other): if type(other) is type(self): return self.msg.hash == other.msg.hash else: return False def __ne__(self, other): return not self.__eq__(other)
[docs] def __str__(self): ''' Format the interaction into a human-readable string. ''' web_interaction = web_interactions.parse(self.msg.command) command = self.msg.command if web_interaction is None else web_interaction.url s = '' s += console.green + "%s" % self.msg.name + console.reset + '\n' s += console.cyan + " Command" + console.reset + " : " + console.yellow + "%s" % command + console.reset + '\n' # noqa s += console.cyan + " Description" + console.reset + " : " + console.yellow + "%s" % self.msg.description + console.reset + '\n' # noqa s += console.cyan + " Icon" + console.reset + " : " + console.yellow + "%s" % str(self.msg.icon.resource_name) + console.reset + '\n' # noqa s += console.cyan + " Rocon URI" + console.reset + " : " + console.yellow + "%s" % self.msg.compatibility + console.reset + '\n' # noqa s += console.cyan + " Namespace" + console.reset + " : " + console.yellow + "%s" % self.msg.namespace + console.reset + '\n' # noqa if self.msg.max == -1: s += console.cyan + " Max" + console.reset + " : " + console.yellow + "infinity" + console.reset + '\n' # noqa else: s += console.cyan + " Max" + console.reset + " : " + console.yellow + "%s" % self.msg.max + console.reset + '\n' # noqa already_prefixed = False for remapping in self.msg.remappings: if not already_prefixed: s += console.cyan + " Remappings" + console.reset + " : " + console.yellow + "%s->%s" % (remapping.remap_from, remapping.remap_to) + console.reset + '\n' # noqa already_prefixed = True else: s += " : " + console.yellow + "%s->%s" % (remapping.remap_from, remapping.remap_to) + console.reset + '\n' # noqa if self.msg.parameters != '': s += console.cyan + " Parameters" + console.reset + " : " + console.yellow + "%s" % self.msg.parameters + console.reset + '\n' # noqa if self.msg.required_pairings: s += console.cyan + " Bringup Pairing " + console.reset + " : " + console.yellow + "%s" % self.msg.bringup_pairing + console.reset + '\n' # noqa s += console.cyan + " Teardown Pairing " + console.reset + ": " + console.yellow + "%s" % self.msg.teardown_pairing + console.reset + '\n' # noqa s += console.cyan + " Required Pairings" + console.reset + ": " + console.yellow + "%s" % self.msg.required_pairings + console.reset + '\n' # noqa s += console.cyan + " Hash" + console.reset + " : " + console.yellow + "%s" % str(self.msg.hash) + console.reset + '\n' # noqa return s