rosros 0.2.5
Simple unified interface to ROS1 / ROS2 Python API
Loading...
Searching...
No Matches
service.py
Go to the documentation of this file.
1"""
2Stand-ins for `rospy.impl.tcpros_service` in ROS2.
3
4------------------------------------------------------------------------------
5This file is part of rosros - simple unified interface to ROS1 / ROS2.
6Released under the BSD License.
7
8@author Erki Suurjaak
9@created 30.05.2022
10@modified 06.12.2023
11------------------------------------------------------------------------------
12"""
13## @namespace rosros.rospify.service
14import abc
15import sys
16import time
17
18import rclpy.client
19
20from .. import ros2
21from . import exceptions
22
23
24def wait_for_service(service, timeout=None):
25 """
26 Blocks until service is available.
27
28 Use this in initialization code if your program depends on a service already running.
29
30 @param service name of service
31 @param timeout timeout time in seconds or Duration, None for no timeout.
32 NOTE: timeout=0 is invalid as wait_for_service actually
33 contacts the service, so non-blocking behavior is not possible.
34 For timeout=0 use cases, just call the service without waiting.
35
36 @throws ROSException if specified timeout is exceeded
37 @throws ROSInterruptException if shutdown interrupts wait
38 """
39 if ros2.to_sec(timeout) == 0.:
40 raise ValueError("timeout must be non-zero")
41 _wait_for_service(service, timeout)
42
43
44def _wait_for_service(service, timeout=None, cls_or_typename=None):
45 """
46 Blocks until service is available.
47
48 Use this in initialization code if your program depends on a service already running.
49
50 @param service name of service
51 @param timeout time to wait at most, as seconds or ROS duration;
52 None or <0 waits forever
53 @param cls_or_typename service type to expect if any,
54 as ROS service class object like `std_msgs.msg.Bool`
55 or service type name like "std_srvs/SetBool"
56
57 @throws ROSException if specified timeout is exceeded
58 @throws ROSInterruptException if shutdown interrupts wait
59 """
60 monostart, timeout = time.monotonic(), ros2.to_sec(timeout)
61 deadline = (monostart + timeout) if timeout and timeout > 0 else 2**31 - 1
62 service, timer, rate, cls, cli = ros2.resolve_name(service), None, None, None, None
63 typename = ros2.canonical(ros2.get_message_type(cls_or_typename) or cls_or_typename or "")
64
65 try:
66 timer = ros2.create_timer(0.1, callback=None)
67 rate = rclpy.timer.Rate(timer, context=ros2.NODE.context)
68
69 while not cls and time.monotonic() < deadline:
70 typenames = sum((tt for n, tt in ros2.get_services() if n == service), [])
71 if typenames and (not typename or typename in typenames):
72 cls = ros2.get_message_class(typename or typenames[0])
73 else: rate.sleep()
74
75 mononow = time.monotonic()
76 if mononow >= deadline:
77 raise exceptions.ROSException("timeout exceeded while waiting for service %s" % service)
78
79 cli = ros2.create_client(service, cls)
80 cli.wait_for_service(None if not timeout else timeout + (mononow - monostart))
81 except rclpy.exceptions.ROSInterruptException as e:
82 raise exceptions.ROSInterruptException(*e.args) from e
83 finally:
84 timer and ros2.destroy_entity(timer)
85 cli and ros2.destroy_entity(cli)
86 timer and ros2.destroy_entity(timer)
87
88
89class Service(abc.ABC):
90 """Stand-in for `rospy.Service`, wrapping the creation of ROS2 service."""
91
92 def __new__(cls, name, service_class, handler,
93 buff_size=None, error_handler=None):
94 """
95 Returns rclpy.service.Service instance.
96
97 @param name service name, absolute or relative
98 @param service_class service definition class
99 @param handler callback function for processing service request.
100 Function takes in a ServiceRequest and returns a ServiceResponse
101 of the appropriate type. Function may also return a list, tuple,
102 or dictionary with arguments to initialize a ServiceResponse
103 instance of the correct type.
104 If handler cannot process request, it may either return None,
105 to indicate failure, or it may raise a rospify.ServiceException
106 to send a specific error message to the client. Returning None
107 is always considered a failure.
108 @param buff_size ignored (ROS1 compatibility stand-in)
109 @param error_handler callback function for handling errors raised in the service code,
110 as fn(exception, exception_type, exception_value, traceback)
111 """
112 if callable(error_handler):
113 def wrapper(req):
114 try: return realhandler(req)
115 except Exception as e:
116 error_handler(*(e, ) + sys.exc_info())
117 raise
118 handler, realhandler = wrapper, handler
119 try:
120 return ros2.create_service(name, service_class, handler)
121 except Exception as e:
122 raise exceptions.ROSException() from e
123
124 @classmethod
125 def __subclasshook__(cls, C):
126 """Returns true if C is a ROS2 service server class, else `NotImplemented`."""
127 return True if issubclass(C, rclpy.service.Service) else NotImplemented
128
129
131class ServiceProxy(abc.ABC):
132 """Stand-in for `rospy.ServiceProxy`, wrapping the creation of ROS2 service client."""
133
134 def __new__(cls, name, service_class, persistent=None, headers=None):
135 """
136 Returns rclpy.client.Client instance.
137
138 @param name name of service to call
139 @param service_class auto-generated service class
140 @param persistent ignored (ROS1 compatibility stand-in)
141 @param headers ignored (ROS1 compatibility stand-in)
142 """
143 return ros2.create_client(name, service_class)
144
145 @classmethod
146 def __subclasshook__(cls, C):
147 """Returns true if C is a ROS2 service client class, else `NotImplemented`."""
148 return True if issubclass(C, rclpy.client.Client) else NotImplemented
149
152 """Exception class for service-related errors"""
153
154
155__all__ = [
156 "Service", "ServiceProxy", "wait_for_service"
157]
Base exception class for ROS clients.
Definition exceptions.py:54
Exception if time moved backwards.
Definition exceptions.py:69
Exception class for service-related errors.
Definition service.py:150
Stand-in for `rospy.Service`, wrapping the creation of ROS2 service.
Definition service.py:89
__new__(cls, name, service_class, handler, buff_size=None, error_handler=None)
Returns rclpy.service.Service instance.
Definition service.py:110
__subclasshook__(cls, C)
Returns true if C is a ROS2 service server class, else `NotImplemented`.
Definition service.py:124
Stand-in for `rospy.ServiceProxy`, wrapping the creation of ROS2 service client.
Definition service.py:130
__subclasshook__(cls, C)
Returns true if C is a ROS2 service client class, else `NotImplemented`.
Definition service.py:145
__new__(cls, name, service_class, persistent=None, headers=None)
Returns rclpy.client.Client instance.
Definition service.py:141
_wait_for_service(service, timeout=None, cls_or_typename=None)
Blocks until service is available.
Definition service.py:59