Source code for capabilities.specs.semantic_interface

# Software License Agreement (BSD License)
#
# Copyright (c) 2013, Open Source Robotics Foundation, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
#  * Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
#  * Redistributions in binary form must reproduce the above
#    copyright notice, this list of conditions and the following
#    disclaimer in the documentation and/or other materials provided
#    with the distribution.
#  * Neither the name of Open Source Robotics Foundation, Inc. nor
#    the names of its contributors may be used to endorse or promote
#    products derived from this software without specific prior
#    written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

# Author: William Woodall <william@osrfoundation.org>

"""
This module implements the Semantic Capability Interface concept

This module contains free functions which serve as factories for the :py:class:`SemanticCapabilityInterface` class.
These factories can take the spec file for a Semantic Capability Interface and create a
:py:class:`SemanticCapabilityInterface` instance out of it.
The :py:class:`SemanticCapabilityInterface` class encapsulates meta information about the semantic interface.
The :py:class:`SemanticCapabilityInterface` class defines of:

- A capability which is being redefined, by name and remappings
- A name
- A global namespace (optional)
- Any ROS Name remappings (optional)

With an Semantic Capability Interface FrontRGBCamera.yaml, like this::

    %YAML 1.1
    ---
    name: FrontRGBCamera
    spec_version: 1
    spec_type: semantic_interface
    description: 'This is semantically the Front RGB camera.'
    redefines: 'a_package/RGBCamera'
    # First the global_namespace is applied to all ROS Names
    global_namespace: 'front_camera'
    # Then individual remappings are done, and override the global_namespace
    remappings:
      topics:
        'camera/image_raw': 'front_camera/image_raw'
        'camera/camera_info': 'front_camera/camera_info'
      services:
        'camera/set_info': 'front_camera/set_info'

You can use this interface like so::

    >>> from pprint import pprint
    >>> from capabilities.specs.semantic_interface import semantic_capability_interface_from_file_path
    >>> sci = semantic_capability_interface_from_file_path('test/specs/semantic_interfaces/FrontRGBCamera.yaml')
    >>> print(sci.redefines)
    a_package/RGBCamera
    >>> print(sci.global_namespace)
    front_camera
    >>> pprint(sci.remappings)
    {'camera/camera_info': 'front_camera/camera_info',
     'camera/image_raw': 'front_camera/image_raw',
     'camera/set_info': 'front_camera/set_info'}

"""

from __future__ import print_function

import os
import yaml

from capabilities.specs.common import validate_spec_name

from capabilities.specs.remappings import RemapCollection

try:
    __basestring = basestring
except NameError:  # pragma: no cover
    __basestring = str


[docs]class InvalidSemanticInterface(Exception): """InvalidSemanticInterface exception""" def __init__(self, msg, file_name): self.file_name = file_name Exception.__init__(self, "In semantic interface spec file '{0}': {1}".format(file_name, msg))
[docs]def semantic_capability_interface_from_file_path(file_path): """Creates a SemanticCapabilityInterface instance from a spec file at a given path See :py:func:`semantic_capability_interface_from_dict` for list of possible exceptions :param file_path: location of the Semantic Capability Interface spec file :type file_path: str :returns: SemanticCapabilityInterface instance, populated with data from the spec file :rtype: :py:class:`SemanticCapabilityInterface` :raises: :py:exc:`OSError` if the given file does not exist """ with open(os.path.abspath(file_path), 'r') as f: return semantic_capability_interface_from_dict(yaml.load(f.read()), file_path)
[docs]def semantic_capability_interface_from_file(file_handle): """Creates a SemanticCapabilityInterface instance from a given spec file handle See :py:func:`semantic_capability_interface_from_dict` for list of possible exceptions :param file_handle: file handle for the Semantic Capability Interface spec file :type file_handle: file :returns: SemanticCapabilityInterface instance, populated with data from the spec file :rtype: :py:class:`SemanticCapabilityInterface` :raises: :py:exc:`OSError` if the given file does not exist """ return semantic_capability_interface_from_dict(yaml.load(file_handle.read()), file_handle.name)
[docs]def semantic_capability_interface_from_string(string, file_name='<string>'): """Creates a SemanticCapabilityInterface instance from a string containing the spec See :py:func:`semantic_capability_interface_from_dict` for list of possible exceptions :param string: Semantic Capability Interface spec :type string: str :param file_name: Name of the file where this spec originated (defaults to '<string>') :type file_name: str :returns: SemanticCapabilityInterface instance, populated with the provided spec :rtype: :py:class:`SemanticCapabilityInterface` :raises: :py:exc:`AttributeError` if the given value for string is not a str """ return semantic_capability_interface_from_dict(yaml.load(string), file_name)
[docs]def semantic_capability_interface_from_dict(spec, file_name='<dict>'): """Creates a SemanticCapabilityInterface instance from a dict version of the spec :param string: Semantic Capability Interface spec :type string: dict :param file_name: Name of the file where this spec originated (defaults to '<dict>') :type file_name: str :returns: SemanticCapabilityInterface instance, populated with the provided spec :rtype: :py:class:`SemanticCapabilityInterface` :raises: :py:exc:`InvalidSemanticInterface` if the spec is not complete or has invalid entries """ if 'spec_type' not in spec: raise InvalidSemanticInterface('No spec type specified', file_name) if spec['spec_type'] != 'semantic_interface': raise InvalidSemanticInterface("Invalid spec type, expected 'semantic_interface' got: '{0}'" .format(spec['spec_type']), file_name) if 'spec_version' not in spec: raise InvalidSemanticInterface('No spec version specified', file_name) spec_version = int(spec['spec_version']) if spec_version != 1: raise InvalidSemanticInterface("Invalid spec version: '{0}'".format(spec_version), file_name) if 'name' not in spec: raise InvalidSemanticInterface('No name specified', file_name) name = spec['name'] if 'redefines' not in spec: raise InvalidSemanticInterface("No redefined capability specified", file_name) redefines = spec['redefines'] try: if isinstance(redefines, __basestring): validate_spec_name(redefines) else: raise InvalidSemanticInterface("Invalid redefines, must be a string", file_name) except (ValueError, AssertionError) as exc: raise InvalidSemanticInterface("Invalid spec name for redefines: " + str(exc), file_name) global_namespace = spec.get('global_namespace', None) description = spec.get('description', 'No description given.') try: semantic_capability_interface = SemanticCapabilityInterface( name, redefines, spec_version, description, global_namespace ) semantic_capability_interface.add_remappings_by_dict(spec.get('remappings', {})) except (AssertionError, ValueError) as exc: raise InvalidSemanticInterface(str(exc), file_name) return semantic_capability_interface
[docs]class SemanticCapabilityInterface(object): """Represents a Semantic Capability Interface A Semantic Capability Interface is defined by: - name (str): name of the redefined interface - redefines (str): name of a capability being redefined - spec_type (str): type of the interface specification (has to be 'semantic_interface') - spec_version (int): version of the interface specification - description (str): free form description of the interface - global_namespace (str or None): (optional) global namespace for all ROS Names, None means no global_namespace - remappings (dict): (optional) map from ROS Names in redefined interface to Names in this interface """ spec_type = 'interface' def __init__(self, name, redefines, spec_version, description=None, global_namespace=None): self.name = name self.redefines = redefines self.spec_version = spec_version self.default_provider = 'unknown' self.description = description self.global_namespace = global_namespace self.__remap_collection = RemapCollection() @property def remappings(self): return self.__remap_collection.remappings def add_remappings_by_dict(self, remappings_dict): self.__remap_collection.add_remappings_by_dict(remappings_dict) def __str__(self): return """Semantic Capability Interface: {{ name: {name} spec version: {spec_version} default provider: {default_provider} redefines: {redefines} global namespace: {global_namespace} description: {description} }}""".format(**self.__dict__)