common_ros.py
Go to the documentation of this file.
00001 # Copyright (c) 2013, Felix Kolbe
00002 # All rights reserved. BSD License
00003 #
00004 # Redistribution and use in source and binary forms, with or without
00005 # modification, are permitted provided that the following conditions
00006 # are met:
00007 #
00008 # * Redistributions of source code must retain the above copyright
00009 #   notice, this list of conditions and the following disclaimer.
00010 #
00011 # * Redistributions in binary form must reproduce the above copyright
00012 #   notice, this list of conditions and the following disclaimer in the
00013 #   documentation and/or other materials provided with the distribution.
00014 #
00015 # * Neither the name of the {organization} nor the names of its
00016 #   contributors may be used to endorse or promote products derived
00017 #   from this software without specific prior written permission.
00018 #
00019 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00020 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00021 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
00022 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
00023 # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
00024 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
00025 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00026 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00027 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00028 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
00029 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00030 
00031 
00032 """
00033 ROS specific specializations of rgoap classes
00034 """
00035 
00036 
00037 import roslib; roslib.load_manifest('rgoap_ros')
00038 import rospy
00039 import rostopic
00040 
00041 from rgoap import Condition, Action
00042 
00043 
00044 import logging
00045 _logger = logging.getLogger('rgoap.ros')
00046 
00047 
00048 
00049 class ROSTopicCondition(Condition):
00050     """Mirrors a ROS message field of a topic as its value.
00051     Note that this Condition's value remains None until a message is received.
00052     """
00053     def __init__(self, state_name, topic, topic_class, field=None, msgeval=None):
00054         Condition.__init__(self, state_name)
00055         self._topic = topic
00056         self._field = field
00057         self._subscriber = rospy.Subscriber(topic, topic_class, self._callback)
00058         if msgeval is None:
00059             assert field is not None
00060             msgeval = rostopic.msgevalgen(field)
00061         self._msgeval = msgeval
00062 
00063         self._value = None
00064 
00065     def __repr__(self):
00066         return '<%s topic=%s field=%s>' % (self.__class__.__name__, self._topic, self._field)
00067 
00068     def _callback(self, msg):
00069         self._value = self._msgeval(msg)
00070 #        _logger.debug("callback with: %s", self._value)
00071 
00072     def get_value(self):
00073         return self._value
00074 
00075 
00076 class ROSTopicAction(Action):
00077 
00078     def __init__(self, topic, topic_class,
00079                  preconditions, effects,
00080                  msg_args=None, msg_cb=None, **kwargs):
00081         """
00082         :param topic_class: Message type, ``Class``
00083         :param msg_args: Arguments to initialize message that is published, ``[val]``
00084         :param msg_cb: callback to replace the default _msg_cb, useful with lambda expressions
00085         """
00086         assert msg_args is None or msg_cb is None, \
00087                     "Only either msg_args or msg_cb may be given, not both"
00088         Action.__init__(self, preconditions, effects, **kwargs)
00089         self._topic = topic
00090         self._topic_class = topic_class
00091         self._msg_args = msg_args
00092         if msg_cb is not None:
00093             self._msg_cb = msg_cb
00094         self._publisher = rospy.Publisher(topic, topic_class, **kwargs)
00095 
00096     def __str__(self):
00097         s = '%s:%s' % (self.__class__.__name__, self._topic)
00098         if self._msg_args is not None:
00099             s += '=%s' % self._msg_args
00100         return s
00101 
00102     def _msg_cb(self, message, next_worldstate):
00103         """Fill and return the given (or a created) message as desired
00104 
00105         Must be implemented and is concerned only if neither msg_args nor msg_cb is given.
00106         """
00107         raise NotImplementedError
00108 
00109     def check_freeform_context(self):
00110         return self._publisher.get_num_connections() > 0  # unsafe
00111 
00112     def run(self, next_worldstate):
00113         _logger.debug("num of subscribers to %s: %d",
00114                       self.__class__.__name__,
00115                       self._publisher.get_num_connections())
00116         if self._msg_args is None:
00117             _logger.info("%s publishes message..", self)
00118             self._publisher.publish(self._msg_cb(self._topic_class(), next_worldstate))
00119             rospy.sleep(1)  # TODO: find solution without sleep
00120         else:
00121             _logger.info("%s publishes message via rostopic..", self)
00122             rostopic.publish_message(self._publisher, self._topic_class, self._msg_args, once=True)
00123             # rostopic sleeps itself
00124 
00125         # TODO: integrate check for asynchronous action bodys


rgoap_ros
Author(s): Felix Kolbe
autogenerated on Sun Oct 5 2014 23:53:07