Skip to content

Instantly share code, notes, and snippets.

@driftregion
Created May 19, 2020 09:39
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save driftregion/14f6da05a71a57ef0804b68e17b06de5 to your computer and use it in GitHub Desktop.
Save driftregion/14f6da05a71a57ef0804b68e17b06de5 to your computer and use it in GitHub Desktop.
Calling a ROS2 service from the callback of another service
import time
from threading import Event
from example_interfaces.srv import AddTwoInts
import rclpy
from rclpy.node import Node
from rclpy.callback_groups import ReentrantCallbackGroup
from rclpy.executors import MultiThreadedExecutor, SingleThreadedExecutor
class ServiceFromService(Node):
def __init__(self):
super().__init__('action_from_service')
self.service_done_event = Event()
self.callback_group = ReentrantCallbackGroup()
self.client = self.create_client(
AddTwoInts,
'/add_two_ints',
callback_group=self.callback_group
)
self.srv = self.create_service(
AddTwoInts,
'add_two_ints_proxy',
self.add_two_ints_proxy_callback,
callback_group=self.callback_group
)
self.srv = self.create_service(
AddTwoInts,
'add_two_ints',
self.add_two_ints_callback,
)
def add_two_ints_callback(self, request, response):
self.get_logger().info('Request received: {} + {}'.format(request.a, request.b))
response.sum = request.a + request.b
return response
def add_two_ints_proxy_callback(self, request, response):
if not self.client.wait_for_service(timeout_sec=5.0):
self.get_logger().error('No action server available')
return response
self.service_done_event.clear()
event=Event()
def done_callback(future):
nonlocal event
event.set()
future = self.client.call_async(request)
future.add_done_callback(done_callback)
# Wait for action to be done
# self.service_done_event.wait()
event.wait()
return future.result()
def get_result_callback(self, future):
# Signal that action is done
self.service_done_event.set()
def main(args=None):
rclpy.init(args=args)
service_from_service = ServiceFromService()
executor = MultiThreadedExecutor()
rclpy.spin(service_from_service, executor)
rclpy.shutdown()
if __name__ == '__main__':
main()
@aserbremen
Copy link

Thanks for this gist, it helped me perform callbacks from within callbacks. I have one question though. What is the purpose of get_result_callback which seems to be never called?

def get_result_callback(self, future):
    # Signal that action is done
    self.service_done_event.set()

@jesusramondovale
Copy link

How can I do the same on C++ nodes? Thank you!!

@smith-doug
Copy link

Thanks for this gist, it helped me perform callbacks from within callbacks. I have one question though. What is the purpose of get_result_callback which seems to be never called?

Probably to have a reusable handler instead of a locally defined function. You can do this with lambda captures, but I'm not sure which version is more messy.

def done_callback(self, future, event):
        event.set()
        event.clear()   # seems to work when immediately cleared, for reuse       

def svc_server_cb(self):
    ....
   event = Event()
   future.add_done_callback(lambda f, e=event: self.done_callback(f, e))
   event.wait()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment